PIJ Logger collects, stores, and streams structured logs from all your projects. Ship faster by spending less time grepping files.
Watch logs flow in live via WebSocket. Pause, resume, and filter without missing a line.
Filter by level, source, and date range. Cursor-paginated so even massive log volumes load fast.
Separate log streams per project, each with its own API tokens and configurable retention.
Create per-environment tokens (live / staging / dev). Revoke instantly without touching your app.
Python and JavaScript SDKs ship with no external dependencies, batching, and automatic retry.
The log terminal is always dark. The rest of the UI follows your OS preference or manual toggle.
Get pinged the moment your app logs an error, with smart flood control. One alert fires immediately; the rest collapse into a digest.
Automatically spots error-rate spikes, volume surges and brand-new failures, with a short AI brief of what happened, the likely cause, and the affected source.
Pick any integration โ they all land in the same real-time stream.
# Send a single log entry
curl -X POST https://log.pijcore.com/api/logs/ingest/ \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '{
"level": "INFO",
"message": "User signed up",
"source": "auth-service",
"meta": {"user_id": 42, "plan": "free"}
}'
# Or batch-send multiple entries at once
curl -X POST https://log.pijcore.com/api/logs/ingest/batch/ \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-d '[
{"level": "DEBUG", "message": "Cache miss", "source": "cache"},
{"level": "ERROR", "message": "Payment failed", "source": "billing"}
]'
from pij_logger import Logger
logger = Logger(
token="<your-token>",
base_url="https://log.pijcore.com",
source="my-service",
batch_size=50, # flush after 50 queued entries
flush_interval=5, # or every 5 seconds
)
# Convenience level methods
logger.debug("Cache hit", meta={"key": "user:42"})
logger.info("User signed up", meta={"user_id": 42})
logger.warning("Rate limit approaching")
logger.error("Payment failed", meta={"code": "CARD_DECLINED"})
logger.critical("Database unreachable")
# Use as context manager โ flushes on exit
with Logger(token="<token>") as log:
log.info("Job started")
# ... do work ...
log.info("Job finished")
import { Logger } from 'pij-logger';
const logger = new Logger({
token: '<your-token>',
baseUrl: 'https://log.pijcore.com',
source: 'my-service',
batchSize: 50,
flushInterval: 5,
});
// Async convenience methods
await logger.debug('Cache hit', { meta: { key: 'user:42' } });
await logger.info('User signed up', { meta: { userId: 42 } });
await logger.warning('Rate limit approaching');
await logger.error('Payment failed', { meta: { code: 'CARD_DECLINED' } });
// Flush before process exit
await logger.close();
# Drop-in logger โ stdlib + certifi. Copy into your project. No SDK needed.
import json, ssl, urllib.request
_UA = "my-app/1.0" # NOT the default: Cloudflare 403s "Python-urllib"
try:
import certifi
_CTX = ssl.create_default_context(cafile=certifi.where())
except Exception: # no certifi -> system trust store
_CTX = ssl.create_default_context()
class PijLogger:
def __init__(self, token, source=None, base_url="https://log.pijcore.com"):
self._url = base_url.rstrip("/") + "/api/logs/ingest/"
self._headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"User-Agent": _UA, # fix: avoid Cloudflare 403
}
self._source = source
def log(self, level, message, meta=None):
body = {"level": level, "message": message}
if self._source: body["source"] = self._source
if meta: body["meta"] = meta
req = urllib.request.Request(
self._url, method="POST", headers=self._headers,
data=json.dumps(body).encode(),
)
try:
urllib.request.urlopen(req, timeout=5, context=_CTX) # fix: TLS
except Exception as e:
print(f"[PijLogger] delivery failed: {e}") # don't swallow silently
def debug(self, m, meta=None): self.log("DEBUG", m, meta)
def info(self, m, meta=None): self.log("INFO", m, meta)
def warning(self, m, meta=None): self.log("WARNING", m, meta)
def error(self, m, meta=None): self.log("ERROR", m, meta)
def critical(self, m, meta=None): self.log("CRITICAL", m, meta)
# Usage
log = PijLogger(token="<your-token>", source="my-service")
log.info("User signed up", meta={"user_id": 42})
log.error("Payment failed", meta={"code": "CARD_DECLINED"})
// Drop-in logger โ plain fetch (Node 18+ / browser), no SDK. Copy into your project.
class PijLogger {
constructor({ token, source, baseUrl = "https://log.pijcore.com" }) {
this._url = baseUrl.replace(/\/$/, "") + "/api/logs/ingest/";
this._headers = {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
"User-Agent": "my-app/1.0", // avoids WAF 403 in Node; browsers ignore it
};
this._source = source;
}
log(level, message, meta) {
const body = { level, message };
if (this._source) body.source = this._source;
if (meta) body.meta = meta;
return fetch(this._url, {
method: "POST",
headers: this._headers,
body: JSON.stringify(body),
}).catch((e) => console.error("[PijLogger] delivery failed:", e)); // don't swallow
}
debug(m, meta) { return this.log("DEBUG", m, meta); }
info(m, meta) { return this.log("INFO", m, meta); }
warning(m, meta) { return this.log("WARNING", m, meta); }
error(m, meta) { return this.log("ERROR", m, meta); }
critical(m, meta) { return this.log("CRITICAL", m, meta); }
}
// Usage โ await in short scripts, or the process may exit before it sends.
const log = new PijLogger({ token: "<your-token>", source: "my-service" });
await log.info("User signed up", { user_id: 42 });
await log.error("Payment failed", { code: "CARD_DECLINED" });
A few things that otherwise make ingestion silently fail โ the snippets above already handle them.
User-Agent. The service is behind a WAF (Cloudflare) that rejects default agents like Python-urllib with 403. That's why curl works but a raw client doesn't โ the snippets set an agent, keep it.CERTIFICATE_VERIFY_FAILED (common on macOS Python builds), run pip install certifi โ the snippet picks it up automatically. On the published SDK: pip install pij-logger[certs].await the call in short scripts, or the process may exit before the request is actually sent./api/logs/ingest/batch/ or the SDK (batching + background flush).