Getting Started
Loguro is a structured log management platform or better said Log OS. This guide walks you through creating a project, generating an API key, and shipping your first log.
Create a project
Sign in to your Loguro account and head to the Console.
- Click New Project, give it a name, and you’re set.
If you wanna be a pro open the command palette and type
--project::create > --keys::create:<name>to create a project and an API key in one go. (the>works as a chain operator, so the project is created first, then the API key is created for it.) - Each project gets its own isolated log stream and API key namespace.
Get an API key (in case you did not chose the path from above)
Inside your project, open the command palette and type --keys::create:<name>, if you don’t pass the <name> directly don’t worry, it will ask for it always.
Browser snippet (zero-config frontend logging)
Drop one <script> tag in your <head> and you’re done. The snippet automatically captures console.error, console.warn, uncaught JS errors, and unhandled promise rejections — no code changes needed.
<script
src="https://logu.ro/loguro.js"
data-api-key="YOUR_BROWSER_KEY"
></script> You can also log manually anywhere in your app via window.Loguro:
Loguro.error('Payment failed', { userId: 42, gateway: 'stripe' });
Loguro.info('Page loaded', { path: window.location.pathname }); Snippet options
| Attribute | Default | Description |
|---|---|---|
data-api-key | — | Required. Your browser API key |
data-intercept | all | Console levels to capture. all, none, or comma-separated: error,warning,info,debug |
data-capture | all | Automatic capture features. all, none, or comma-separated (see below) |
data-trace | true | Session trace ID. When enabled, all logs from the same tab share a trace ID searchable via trace:"<id>" |
data-max-per-minute | 1000 | Client-side rate limit cap (ingest hard limit is 10k/min per key) |
data-intercept level mapping:
| Value | Captures |
|---|---|
error | console.error, uncaught JS errors, unhandled promise rejections |
warning | console.warn |
info | console.info |
debug | console.log, console.debug |
data-capture feature mapping:
| Value | Default | Captures |
|---|---|---|
context | ✅ | Page URL, path, referrer, viewport, user agent on every log |
network | ✅ | Failed fetch and XHR requests (4xx/5xx + network errors) with URL, status, duration |
performance | ✅ | Long tasks (>50ms JS blocks), page load timing (TTFB, DOM interactive, load) |
vitals | ✅ | Core Web Vitals — LCP, CLS, FID/INP via PerformanceObserver |
rage-clicks | ✅ | 3+ rapid clicks on the same element within 500ms |
breadcrumbs | ❌ | Click trail and network requests sent alongside each error log |
Session tracing
By default, the snippet generates a single UUID when the page loads and attaches it to every log sent from that tab as the X-Request-Id header. Loguro stores this as the trace ID, so you can find everything a user did in a session by filtering with trace:"<id>" in the console.
The trace ID is reset on each page load (refresh or new tab). It is never persisted to localStorage or cookies.
To opt out — for example if you need GDPR compliance and cannot correlate sessions without consent — set data-trace="false":
<script src="https://logu.ro/loguro.js" data-api-key="..." data-trace="false"></script> You can also send a trace ID manually from your backend by passing the X-Request-Id header on ingest requests. Loguro will use whatever value you provide, so you can correlate frontend and backend logs under the same trace.
<!-- Default: all features on, breadcrumbs off -->
<script src="https://logu.ro/loguro.js" data-api-key="..."></script>
<!-- Errors only, no automatic capture features -->
<script src="https://logu.ro/loguro.js" data-api-key="..." data-intercept="error" data-capture="none"></script>
<!-- Everything including breadcrumbs -->
<script src="https://logu.ro/loguro.js" data-api-key="..." data-capture="context,network,performance,vitals,rage-clicks,breadcrumbs"></script>
<!-- Only network errors and context -->
<script src="https://logu.ro/loguro.js" data-api-key="..." data-intercept="none" data-capture="network,context"></script> The snippet has a built-in circuit breaker and rate limiter — if the ingest service is unreachable it backs off automatically and never throws or slows down your app.
Browser keys (frontend logging)
By default, API keys are server keys — meant to be kept secret on your backend. If you want to send logs directly from a browser or frontend app, create a browser key instead.
Browser keys are safe to expose in client-side code because they are locked to a list of allowed origins. The ingest service checks the Origin header on every request and rejects anything not on the whitelist.
To create a browser key, open the command palette and type --keys::create:browser:<name>, or use the API Keys page and select the Browser type when creating a key. You’ll be asked to provide a comma-separated list of allowed origins (e.g. https://myapp.com, http://localhost:3000).
// Example: send a log from the browser
fetch('https://ingest.logu.ro', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_BROWSER_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
level: 'error',
message: 'Unhandled exception',
context: { path: window.location.pathname }
})
}); The
Originheader is set automatically by the browser and cannot be spoofed by JavaScript — this is what makes browser keys safe to use in public code.
Send your first log
Loguro accepts structured JSON, OTLP and OTLP proto by default, you just need to send it. POST to the ingest endpoint:
curl -X POST https://ingest.logu.ro \
-H "Authorization: Bearer $LOGURO_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Request-Id: Optional" \
-d '{
"level": "info",
"message": "Server started",
"timestamp": "", // If not, i will put it for you, but the timezones might be off
"context": { // Anything you want in the context, send relevant data, it will be searchable by context.something:"everything"
"port": 3000,
"env": "production"
}
}' A 202 means the log was accepted. Head to your project’s Logs view and you should see it appear within miliseconds.
Log levels
Loguro supports six log levels. Use them consistently across your services:
| Level | When to use |
|---|---|
debug | Verbose diagnostic information |
info | Normal operational events |
warning | Unexpected but recoverable conditions |
error | Failures that need attention |
critical | System-wide failures, requires immediate action |
heartbeat | Periodic health pulse from a service — used to detect when a service goes silent. See Alerting for how to trigger alerts when heartbeats stop arriving. |
Using context
The context field is a flat or nested JSON object. Any key you include becomes searchable in the filter bar using the context.key:value syntax.
{
"level": "error",
"message": "Payment failed",
"context": {
"userId": 42,
"orderId": "ord_9xk2",
"gateway": "stripe",
"errorCode": "card_declined"
}
} You can then search for context.gateway:"stripe" or context.errorCode:"card_declined" or context.status_code:404 directly in the console.
Batch ingestion
To send multiple logs in a single request, POST an array to /batch:
curl -X POST https://ingest.logu.ro/batch \
-H "Authorization: Bearer $LOGURO_API_KEY" \
-H "Content-Type: application/json" \
-d '[
{ "level": "info", "message": "Request received", "context": { "path": "/api/users" } },
{ "level": "error", "message": "DB timeout", "context": { "query": "SELECT *", "ms": 5100 } }
]' Each object in the array follows the same schema as a single log. The response is still 202 Accepted.
OTLP support
Loguro accepts OpenTelemetry logs natively. The format is detected automatically via Content-Type:
| Content-Type | Format |
|---|---|
application/json | Plain Loguro JSON or OTLP JSON |
application/x-protobuf | OTLP Protobuf binary |
OTLP JSON example (OpenTelemetry Log Data Model):
curl -X POST https://ingest.logu.ro \
-H "Authorization: Bearer $LOGURO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resourceLogs": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "payments" } }] },
"scopeLogs": [{
"logRecords": [{
"timeUnixNano": "1711234567000000000",
"severityText": "ERROR",
"body": { "stringValue": "Payment failed" },
"attributes": [
{ "key": "userId", "value": { "intValue": "42" } }
]
}]
}]
}]
}' Limits & quotas
| Limit | Value |
|---|---|
| Max payload size | 1 MB per request |
| Rate limit | 10,000 requests/min per API key |
When your project has consumed more than 80% of its monthly log quota, the ingest service adds a response header:
X-Quota-Warning: 85 The value is the percentage used. Monitor this header to avoid hitting your plan limit.
Error responses
| Status | Meaning |
|---|---|
202 | Log accepted |
401 | Missing or invalid API key |
402 | Subscription inactive or monthly quota exhausted |
413 | Payload exceeds 1 MB |
429 | Rate limit exceeded — back off and retry |
500 | Ingest service error |
Next steps
- Read the Query Syntax guide to master the advanced filter bar
- Learn how to connect Integrations for Slack and issue trackers