Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Dockerfile.metrics
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM evoapicloud/evolution-api:latest AS base
WORKDIR /evolution

# Copiamos apenas o necessário para recompilar o dist com as mudanças locais
COPY tsconfig.json tsup.config.ts package.json ./
COPY src ./src

# Recompila usando os node_modules já presentes na imagem base
RUN npm run build

# Runtime final: reaproveita a imagem oficial e apenas sobrepõe o dist
FROM evoapicloud/evolution-api:latest AS final
WORKDIR /evolution
COPY --from=base /evolution/dist ./dist

ENV PROMETHEUS_METRICS=true

# Entrada original da imagem oficial já sobe o app em /evolution

2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: "3.8"
services:
api:
container_name: evolution_api
image: evoapicloud/evolution-api:latest
image: evolution/api:metrics
restart: always
depends_on:
- redis
Expand Down
60 changes: 60 additions & 0 deletions src/api/routes/index.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ChannelRouter } from '@api/integrations/channel/channel.router';
import { ChatbotRouter } from '@api/integrations/chatbot/chatbot.router';
import { EventRouter } from '@api/integrations/event/event.router';
import { StorageRouter } from '@api/integrations/storage/storage.router';
import { waMonitor } from '@api/server.module';
import { configService } from '@config/env.config';
import { fetchLatestWaWebVersion } from '@utils/fetchLatestWaWebVersion';
import { Router } from 'express';
Expand Down Expand Up @@ -42,6 +43,65 @@ const telemetry = new Telemetry();

const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));

// Expose Prometheus metrics when enabled by env flag
if (process.env.PROMETHEUS_METRICS === 'true') {
router.get('/metrics', async (req, res) => {
res.set('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');

const escapeLabel = (value: unknown) =>
String(value ?? '')
.replace(/\\/g, '\\\\')
.replace(/\n/g, '\\n')
.replace(/"/g, '\\"');

const lines: string[] = [];

const clientName = process.env.DATABASE_CONNECTION_CLIENT_NAME || 'unknown';
const serverUrl = serverConfig.URL || '';

// environment info
lines.push('# HELP evolution_environment_info Environment information');
lines.push('# TYPE evolution_environment_info gauge');
lines.push(
`evolution_environment_info{version="${escapeLabel(packageJson.version)}",clientName="${escapeLabel(
clientName,
)}",serverUrl="${escapeLabel(serverUrl)}"} 1`,
);

const instances = (waMonitor && waMonitor.waInstances) || {};
const instanceEntries = Object.entries(instances);

// total instances
lines.push('# HELP evolution_instances_total Total number of instances');
lines.push('# TYPE evolution_instances_total gauge');
lines.push(`evolution_instances_total ${instanceEntries.length}`);

// per-instance status
lines.push('# HELP evolution_instance_up 1 if instance state is open, else 0');
lines.push('# TYPE evolution_instance_up gauge');
lines.push('# HELP evolution_instance_state Instance state as a labelled metric');
lines.push('# TYPE evolution_instance_state gauge');

for (const [name, instance] of instanceEntries) {
const state = instance?.connectionStatus?.state || 'unknown';
const integration = instance?.integration || '';
const up = state === 'open' ? 1 : 0;

lines.push(
`evolution_instance_up{instance="${escapeLabel(name)}",integration="${escapeLabel(integration)}"} ${up}`,
);
lines.push(
`evolution_instance_state{instance="${escapeLabel(name)}",integration="${escapeLabel(
integration,
)}",state="${escapeLabel(state)}"} 1`,
);
}

res.send(lines.join('\n') + '\n');
});
}

if (!serverConfig.DISABLE_MANAGER) router.use('/manager', new ViewsRouter().router);

router.get('/assets/*', (req, res) => {
Expand Down