@@ -5,6 +5,7 @@ import { ChannelRouter } from '@api/integrations/channel/channel.router';
55import { ChatbotRouter } from '@api/integrations/chatbot/chatbot.router' ;
66import { EventRouter } from '@api/integrations/event/event.router' ;
77import { StorageRouter } from '@api/integrations/storage/storage.router' ;
8+ import { waMonitor } from '@api/server.module' ;
89import { configService } from '@config/env.config' ;
910import { fetchLatestWaWebVersion } from '@utils/fetchLatestWaWebVersion' ;
1011import { Router } from 'express' ;
@@ -42,6 +43,65 @@ const telemetry = new Telemetry();
4243
4344const packageJson = JSON . parse ( fs . readFileSync ( './package.json' , 'utf8' ) ) ;
4445
46+ // Expose Prometheus metrics when enabled by env flag
47+ if ( process . env . PROMETHEUS_METRICS === 'true' ) {
48+ router . get ( '/metrics' , async ( req , res ) => {
49+ res . set ( 'Content-Type' , 'text/plain; version=0.0.4; charset=utf-8' ) ;
50+ res . set ( 'Cache-Control' , 'no-cache, no-store, must-revalidate' ) ;
51+
52+ const escapeLabel = ( value : unknown ) =>
53+ String ( value ?? '' )
54+ . replace ( / \\ / g, '\\\\' )
55+ . replace ( / \n / g, '\\n' )
56+ . replace ( / " / g, '\\"' ) ;
57+
58+ const lines : string [ ] = [ ] ;
59+
60+ const clientName = process . env . DATABASE_CONNECTION_CLIENT_NAME || 'unknown' ;
61+ const serverUrl = serverConfig . URL || '' ;
62+
63+ // environment info
64+ lines . push ( '# HELP evolution_environment_info Environment information' ) ;
65+ lines . push ( '# TYPE evolution_environment_info gauge' ) ;
66+ lines . push (
67+ `evolution_environment_info{version="${ escapeLabel ( packageJson . version ) } ",clientName="${ escapeLabel (
68+ clientName ,
69+ ) } ",serverUrl="${ escapeLabel ( serverUrl ) } "} 1`,
70+ ) ;
71+
72+ const instances = ( waMonitor && waMonitor . waInstances ) || { } ;
73+ const instanceEntries = Object . entries ( instances ) ;
74+
75+ // total instances
76+ lines . push ( '# HELP evolution_instances_total Total number of instances' ) ;
77+ lines . push ( '# TYPE evolution_instances_total gauge' ) ;
78+ lines . push ( `evolution_instances_total ${ instanceEntries . length } ` ) ;
79+
80+ // per-instance status
81+ lines . push ( '# HELP evolution_instance_up 1 if instance state is open, else 0' ) ;
82+ lines . push ( '# TYPE evolution_instance_up gauge' ) ;
83+ lines . push ( '# HELP evolution_instance_state Instance state as a labelled metric' ) ;
84+ lines . push ( '# TYPE evolution_instance_state gauge' ) ;
85+
86+ for ( const [ name , instance ] of instanceEntries ) {
87+ const state = instance ?. connectionStatus ?. state || 'unknown' ;
88+ const integration = instance ?. integration || '' ;
89+ const up = state === 'open' ? 1 : 0 ;
90+
91+ lines . push (
92+ `evolution_instance_up{instance="${ escapeLabel ( name ) } ",integration="${ escapeLabel ( integration ) } "} ${ up } ` ,
93+ ) ;
94+ lines . push (
95+ `evolution_instance_state{instance="${ escapeLabel ( name ) } ",integration="${ escapeLabel (
96+ integration ,
97+ ) } ",state="${ escapeLabel ( state ) } "} 1`,
98+ ) ;
99+ }
100+
101+ res . send ( lines . join ( '\n' ) + '\n' ) ;
102+ } ) ;
103+ }
104+
45105if ( ! serverConfig . DISABLE_MANAGER ) router . use ( '/manager' , new ViewsRouter ( ) . router ) ;
46106
47107router . get ( '/assets/*' , ( req , res ) => {
0 commit comments