Skip to content
lokalise-observability logo

Lokalise Observability

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 metri...

SKILL.md

Full skill 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

MetricTypeDescription
lokalise_requests_totalCounterTotal API requests
lokalise_request_duration_secondsHistogramRequest latency
lokalise_errors_totalCounterError count by type
lokalise_rate_limit_remainingGaugeRate limit headroom
lokalise_keys_totalGaugeTotal translation keys
lokalise_translation_coverageGaugeTranslation progress
lokalise_webhook_events_totalCounterWebhook events received

Instructions

Step 1: Prometheus Metrics Setup

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

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();
  }
}

// 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

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

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!,
    });

    // 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

# 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

IssueCauseSolution
Missing metricsNo instrumentationWrap client calls
Log floodingVerbose levelAdjust log level
Alert stormsWrong thresholdsTune alert rules
Health false positiveTimeout too shortIncrease 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",
      "targets": [{
        "expr": "lokalise_translation_coverage"
      }]
    }
  ]
}

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
    );
  }
}

// Run periodically
setInterval(() => {
  updateTranslationCoverageMetrics(process.env.LOKALISE_PROJECT_ID!);
}, 300000);  // Every 5 minutes

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.

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View