| name | lokalise-observability |
| description | Set up comprehensive observability for Lokalise integrations with metrics, traces, and alerts.
Use when implementing monitoring for Lokalise operations, setting up dashboards,
or configuring alerting for Lokalise integration health.
Trigger with phrases like "lokalise monitoring", "lokalise metrics",
"lokalise observability", "monitor lokalise", "lokalise alerts", "lokalise tracing".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Lokalise Observability
Overview
Set up comprehensive observability for Lokalise integrations with metrics, logging, and alerting.
Prerequisites
- Prometheus or compatible metrics backend
- Logging infrastructure (ELK, Datadog, etc.)
- Grafana or similar dashboarding tool
- AlertManager configured
Key Metrics
| Metric | Type | Description |
|---|
lokalise_requests_total | Counter | Total API requests |
lokalise_request_duration_seconds | Histogram | Request latency |
lokalise_errors_total | Counter | Error count by type |
lokalise_rate_limit_remaining | Gauge | Rate limit headroom |
lokalise_keys_total | Gauge | Total translation keys |
lokalise_translation_coverage | Gauge | Translation progress |
lokalise_webhook_events_total | Counter | Webhook events received |
Instructions
Step 1: Prometheus Metrics Setup
import { Registry, Counter, Histogram, Gauge } from "prom-client";
const registry = new Registry();
const requestCounter = new Counter({
name: "lokalise_requests_total",
help: "Total Lokalise API requests",
labelNames: ["method", "endpoint", "status"],
registers: [registry],
});
const requestDuration = new Histogram({
name: "lokalise_request_duration_seconds",
help: "Lokalise request duration",
labelNames: ["method", "endpoint"],
buckets: [0.1, 0.25, 0.5, 1, 2.5, 5, 10],
registers: [registry],
});
const errorCounter = new Counter({
name: "lokalise_errors_total",
help: "Lokalise errors by type",
labelNames: ["error_type", "endpoint"],
: [registry],
});
rateLimitRemaining = ({
: ,
: ,
: [registry],
});
translationCoverage = ({
: ,
: ,
: [, ],
: [registry],
});
Step 2: Instrumented Client Wrapper
import { LokaliseApi } from "@lokalise/node-api";
export async function instrumentedRequest<T>(
method: string,
endpoint: string,
operation: () => Promise<T>
): Promise<T> {
const timer = requestDuration.startTimer({ method, endpoint });
try {
const result = await operation();
requestCounter.inc({ method, endpoint, status: "success" });
return result;
} catch (error: any) {
requestCounter.inc({ method, endpoint, status: "error" });
errorCounter.inc({
error_type: error.code?.toString() || "unknown",
endpoint,
});
throw error;
} finally {
timer();
}
}
export class InstrumentedLokaliseClient {
private client: LokaliseApi;
constructor(apiKey: string) {
. = ({ apiKey });
}
() {
(, ,
..().()
);
}
() {
(, ,
..().({ : projectId })
);
}
() {
(, ,
..().(projectId, options)
);
}
}
Step 3: Structured Logging
import pino from "pino";
const logger = pino({
name: "lokalise",
level: process.env.LOG_LEVEL || "info",
formatters: {
level: (label) => ({ level: label }),
},
});
interface LokaliseLogContext {
operation: string;
projectId?: string;
keyId?: number;
locale?: string;
duration?: number;
error?: string;
}
export function logLokaliseOperation(
level: "info" | "warn" | "error",
message: string,
context: LokaliseLogContext
) {
logger[level]({
service: "lokalise",
...context,
timestamp: new Date().toISOString(),
}, message);
}
logLokaliseOperation("info", "Translation sync completed", {
operation: ,
: ,
: ,
});
(, , {
: ,
: ,
: ,
});
Step 4: Health Check Endpoint
interface LokaliseHealth {
status: "healthy" | "degraded" | "unhealthy";
latencyMs: number;
rateLimitRemaining?: number;
lastSync?: string;
error?: string;
}
export async function checkLokaliseHealth(): Promise<LokaliseHealth> {
const start = Date.now();
try {
const client = new LokaliseApi({
apiKey: process.env.LOKALISE_API_TOKEN!,
});
await client.projects().list({ limit: 1 });
const latencyMs = Date.now() - start;
return {
status: latencyMs < 2000 ? "healthy" : "degraded",
latencyMs,
lastSync: process.env.LAST_TRANSLATION_SYNC,
};
} catch (error: any) {
{
: ,
: .() - start,
: error.,
};
}
}
app.(, (req, res) => {
health = ();
statusCode = health. === ? : ;
res.(statusCode).(health);
});
Step 5: Alert Rules
groups:
- name: lokalise_alerts
rules:
- alert: LokaliseHighErrorRate
expr: |
rate(lokalise_errors_total[5m]) /
rate(lokalise_requests_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Lokalise error rate > 5%"
description: "Error rate is {{ $value | printf \"%.2f\" }}%"
- alert: LokaliseHighLatency
expr: |
histogram_quantile(0.95,
rate(lokalise_request_duration_seconds_bucket[5m])
) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "Lokalise P95 latency > 3s"
- alert: LokaliseRateLimitLow
expr: lokalise_rate_limit_remaining < 2
for: 1m
labels:
severity: warning
annotations:
summary:
Output
- Metrics collection enabled
- Structured logging implemented
- Health check endpoint
- Alert rules deployed
Error Handling
| Issue | Cause | Solution |
|---|
| Missing metrics | No instrumentation | Wrap client calls |
| Log flooding | Verbose level | Adjust log level |
| Alert storms | Wrong thresholds | Tune alert rules |
| Health false positive | Timeout too short | Increase timeout |
Examples
Grafana Dashboard Queries
{
"panels": [
{
"title": "Lokalise Request Rate",
"targets": [{
"expr": "rate(lokalise_requests_total[5m])"
}]
},
{
"title": "Lokalise Latency P50/P95/P99",
"targets": [
{ "expr": "histogram_quantile(0.50, rate(lokalise_request_duration_seconds_bucket[5m]))" },
{ "expr": "histogram_quantile(0.95, rate(lokalise_request_duration_seconds_bucket[5m]))" },
{ "expr": "histogram_quantile(0.99, rate(lokalise_request_duration_seconds_bucket[5m]))" }
]
},
{
"title": "Translation Coverage by Language"
Collect Translation Coverage
async function updateTranslationCoverageMetrics(projectId: string) {
const client = new LokaliseApi({
apiKey: process.env.LOKALISE_API_TOKEN!,
});
const languages = await client.languages().list({ project_id: projectId });
for (const lang of languages.items) {
translationCoverage.set(
{ project: projectId, language: lang.lang_iso },
lang.statistics?.progress ?? 0
);
}
}
setInterval(() => {
updateTranslationCoverageMetrics(process.env.LOKALISE_PROJECT_ID!);
}, 300000);
Metrics Endpoint
app.get("/metrics", async (req, res) => {
res.set("Content-Type", registry.contentType);
res.send(await registry.metrics());
});
Resources
Next Steps
For incident response, see lokalise-incident-runbook.