๐Ÿ“… let's chat! explore the endless possibilities creating industries that don't exist. click here

lokalise-observability

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>

Allowed Tools

No tools specified

Provided by Plugin

lokalise-pack

Claude Code skill pack for Lokalise (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the lokalise-pack plugin:

/plugin install lokalise-pack@claude-code-plugins-plus

Click to copy

Instructions

# 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 ```typescript import { Registry, Counter, Histogram, Gauge } from "prom-client"; const registry = new Registry(); // Request metrics 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"], registers: [registry], }); // Rate limit tracking const rateLimitRemaining = new Gauge({ name: "lokalise_rate_limit_remaining", help: "Remaining rate limit quota", registers: [registry], }); // Translation metrics const translationCoverage = new Gauge({ name: "lokalise_translation_coverage", help: "Translation coverage percentage", labelNames: ["project", "language"], registers: [registry], }); ``` ### Step 2: Instrumented Client Wrapper ```typescript import { LokaliseApi } from "@lokalise/node-api"; export async function instrumentedRequest( method: string, endpoint: string, operation: () => Promise ): Promise { 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(); } } // Usage export class InstrumentedLokaliseClient { private client: LokaliseApi; constructor(apiKey: string) { this.client = new LokaliseApi({ apiKey }); } async listProjects() { return instrumentedRequest("GET", "/projects", () => this.client.projects().list() ); } async listKeys(projectId: string) { return instrumentedRequest("GET", "/keys", () => this.client.keys().list({ project_id: projectId }) ); } async downloadFiles(projectId: string, options: any) { return instrumentedRequest("POST", "/files/download", () => this.client.files().download(projectId, options) ); } } ``` ### Step 3: Structured Logging ```typescript 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); } // Usage logLokaliseOperation("info", "Translation sync completed", { operation: "sync", projectId: "123456.abc", duration: 1500, }); logLokaliseOperation("error", "API request failed", { operation: "listKeys", projectId: "123456.abc", error: "Rate limit exceeded", }); ``` ### Step 4: Health Check Endpoint ```typescript interface LokaliseHealth { status: "healthy" | "degraded" | "unhealthy"; latencyMs: number; rateLimitRemaining?: number; lastSync?: string; error?: string; } export async function checkLokaliseHealth(): Promise { const start = Date.now(); try { const client = new LokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN!, }); // Quick connectivity test 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) { return { status: "unhealthy", latencyMs: Date.now() - start, error: error.message, }; } } // Express endpoint app.get("/health/lokalise", async (req, res) => { const health = await checkLokaliseHealth(); const statusCode = health.status === "healthy" ? 200 : 503; res.status(statusCode).json(health); }); ``` ### Step 5: Alert Rules ```yaml # prometheus/lokalise_alerts.yml 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: "Lokalise rate limit nearly exhausted" - alert: LokaliseDown expr: up{job="lokalise"} == 0 for: 1m labels: severity: critical annotations: summary: "Lokalise integration is down" ``` ## 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 ```json { "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", "targets": [{ "expr": "lokalise_translation_coverage" }] } ] } ``` ### Collect Translation Coverage ```typescript 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 ); } } // Run periodically setInterval(() => { updateTranslationCoverageMetrics(process.env.LOKALISE_PROJECT_ID!); }, 300000); // Every 5 minutes ``` ### Metrics Endpoint ```typescript app.get("/metrics", async (req, res) => { res.set("Content-Type", registry.contentType); res.send(await registry.metrics()); }); ``` ## Resources - [Prometheus Best Practices](https://prometheus.io/docs/practices/naming/) - [Grafana Dashboards](https://grafana.com/docs/grafana/latest/dashboards/) - [pino Logger](https://getpino.io/) ## Next Steps For incident response, see `lokalise-incident-runbook`.

Skill file: plugins/saas-packs/lokalise-pack/skills/lokalise-observability/SKILL.md