Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
5264063
feat: add audit log for tracking user actions (#94)
Polliog Feb 22, 2026
0cbea5f
Merge pull request #143 from logtide-dev/feature/94-feature-audit-log…
Polliog Feb 22, 2026
b5ac10e
add service dependency graph page (#40)
Polliog Feb 22, 2026
a2721b4
feat: updated changelog for added service dependency graph and correl…
Polliog Feb 22, 2026
f4ab7c4
Merge pull request #144 from logtide-dev/feature/40-feature-service-d…
Polliog Feb 22, 2026
d77a822
feat: implement OTLP metrics ingestion with API routes and database s…
Polliog Feb 22, 2026
6d58633
add tests for OTLP metrics ingestion (188 tests)
Polliog Feb 22, 2026
9df0db1
Merge pull request #145 from logtide-dev/feature/4-feature-otlp-metri…
Polliog Feb 22, 2026
7ee8c18
Merge branch 'feature/4-feature-otlp-metrics-ingestion' into develop
Polliog Feb 22, 2026
4dc65d7
feat: create database if it doesn't exist during initialization
Polliog Feb 22, 2026
4221347
feat: update Dockerfile to include tsx for scripts and copy source sc…
Polliog Feb 22, 2026
72a5df0
add ux restructuring design doc
Polliog Feb 22, 2026
7a79a98
add ux restructuring implementation plan
Polliog Feb 22, 2026
fa07bc2
add observe context store
Polliog Feb 22, 2026
f559480
add observe context bar component
Polliog Feb 22, 2026
edb63e3
restructure sidebar, wire context bar
Polliog Feb 22, 2026
6a72c77
update command palette nav items
Polliog Feb 22, 2026
51d3abb
merge service map into traces, use observe context store
Polliog Feb 22, 2026
fc072c5
integrate observe context into metrics, fix trace link
Polliog Feb 22, 2026
746e822
integrate observe context into search, fix error handling
Polliog Feb 23, 2026
d8c9c92
move sigma rules from alerts to security section
Polliog Feb 23, 2026
c726da8
simplify project pages, remove duplicate log viewer
Polliog Feb 23, 2026
1d6ac8b
fix locale bug, silent errors, empty states, remove dead code
Polliog Feb 23, 2026
c9f8a07
remove observe context bar, restore per-page filters
Polliog Feb 23, 2026
f81dbd8
refactor: remove layout store dependencies and simplify page structur…
Polliog Feb 23, 2026
287843a
Merge pull request #146 from logtide-dev/feature/0.7-ux
Polliog Feb 26, 2026
cfd0b29
Merge branch 'main' into develop
Polliog Feb 26, 2026
96df5f3
chore: update changelog and version to 0.7.0, add new features and im…
Polliog Feb 26, 2026
050822c
fix: add tsx as a production dependency and update Dockerfile instruc…
Polliog Feb 26, 2026
37bccae
fix: update pagination to show total counts for logs and incidents
Polliog Feb 26, 2026
d4915cd
fix: normalize bucket keys in admin dashboard timeline to eliminate gaps
Polliog Feb 26, 2026
6fcbddb
fix: remove unused ArrowLeft icon from security incidents page
Polliog Feb 26, 2026
477b981
fix: clean up all frontend warnings
Polliog Feb 26, 2026
58b8488
add ascii art banner on server startup
Polliog Feb 26, 2026
42ae0eb
add ascii art banner to worker startup
Polliog Feb 26, 2026
73a0cfa
feat: enhance batch ingestion endpoint to support flexible payload fo…
Polliog Feb 26, 2026
604440e
fix: update imports from 'lucide-svelte' to '@lucide/svelte' across m…
Polliog Feb 26, 2026
fe89ea7
delete old md stuff
Polliog Feb 26, 2026
59b2a8b
fix: correct typos in OTLP ingestion and authentication, update LogTi…
Polliog Feb 28, 2026
d3ef576
ignore test-vector dir
Polliog Feb 28, 2026
084c59d
fix: update LogTide SDK usage in examples and improve axis label form…
Polliog Feb 28, 2026
367df24
fix: update Dockerfile for improved pnpm installation and build proce…
Polliog Feb 28, 2026
3d2bff5
fix: update Dockerfile for improved pnpm installation and build proce…
Polliog Feb 28, 2026
a6d4fce
test: enhance ingestion and metric transformer tests for edge cases a…
Polliog Mar 1, 2026
eade785
test: enhance ingestion and metric transformer tests for edge cases a…
Polliog Mar 1, 2026
f5b86aa
test: enhance ingestion and metric transformer tests for edge cases a…
Polliog Mar 1, 2026
5fdbc23
test: enhance ingestion and metric transformer tests for edge cases a…
Polliog Mar 1, 2026
009a2bf
fix: conditionally set Content-Type header based on request body pres…
Polliog Mar 1, 2026
b0142f1
test: adjust log creation timestamps to avoid future timestamps near …
Polliog Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Force LF line endings for files that break with CRLF
*.sh text eol=lf
Dockerfile text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ claude.md
/packages/backend/data/
/GEMINI.md
/packages/backend/coverage-output.txt
/docs/plans/
/test-vector/
87 changes: 87 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,93 @@ All notable changes to LogTide will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.0] - 2026-02-26

### Added

- **OTLP Metrics Ingestion** (#4): complete OpenTelemetry metrics support, closing the observability stack (logs + traces + metrics)
- `POST /v1/otlp/metrics` endpoint with protobuf and JSON support (gzip compression on both)
- All 5 OTLP metric types: gauge, sum, histogram, exponential histogram, summary
- Exemplar support with trace/span correlation (click metric → see related traces)
- `metrics` + `metric_exemplars` TimescaleDB hypertables with compression (7d) and retention (90d)
- Full ClickHouse support via reservoir abstraction
- Query API: `GET /api/v1/metrics/names`, `/labels/keys`, `/labels/values`, `/data`, `/aggregate`
- 7 aggregation intervals (1m–1w) and 6 aggregation functions (avg, sum, min, max, count, last)
- Group-by label support for multi-series visualization
- Svelte store + API client ready for frontend integration
- 118+ tests covering ingestion, transformation, query, and both storage engines

- **Service Dependency Graph & Correlation Analysis** (#40): dedicated service map visualizing microservice interactions
- Force-directed graph (ECharts) built from span parent-child relationships + log co-occurrence analysis
- Enriched backend endpoint `GET /api/v1/traces/service-map` runs 3 parallel queries: span deps (reservoir), per-service health stats (continuous aggregates), log co-occurrence (trace_id self-join)
- Health color-coding on nodes: green (<1% errors), amber (1-10%), red (>10%)
- Click-to-inspect side panel showing error rate, avg/p95 latency, total calls, upstream/downstream edges
- Dashed edges for log correlation, solid for span-based dependencies
- PNG export, time range filtering, project picker

- **Audit Log**: comprehensive audit trail tracking all user actions across the platform for compliance and security (SOC 2, ISO 27001, HIPAA)
- Tracks 4 event categories: log access, config changes, user management, data modifications
- Logged actions: login, logout, register, create/update/delete organizations, create/update/delete projects, create/revoke API keys, member role changes, member removal, leave organization, admin operations
- TimescaleDB hypertable with 7-day chunks, automatic compression (30 days), and retention policy (365 days)
- High-performance in-memory buffer with periodic flush (50 entries or 1s interval) for non-blocking writes
- Accessible to organization owners and admins via Organization Settings
- Expandable table rows showing full event details: metadata, resource IDs, user agent, IP address
- Category and action filters
- CSV export with current filters applied (up to 10k rows)
- Export actions are themselves audit-logged (meta-meta logging)

### Changed

- **Batch ingestion endpoint**: `POST /api/v1/ingest` now accepts flexible payload formats for better collector compatibility (Vector, Fluent Bit, etc.)
- Standard format: `{"logs": [{...}]}` (unchanged)
- Direct array: `[{log1}, {log2}]` (Vector with `codec: json`)
- Wrapped array: `[{"logs": [{...}]}, ...]` (Vector with VRL wrapping)
- Array formats auto-normalize fields via `normalizeLogData` (auto-generates `time`, normalizes `level`, extracts `service`)

- **UX Restructuring**: major navigation and page layout overhaul for better discoverability
- **Sidebar grouped into sections**: Observe (Logs, Traces, Metrics, Errors), Detect (Alerts, Security), Manage (Projects, Settings) — replaces flat 11-item list
- **Service Map merged into Traces**: list/map view toggle on the Traces page instead of a separate route
- **Sigma Rules moved to Security**: Security page now has sub-nav with Dashboard, Rules, Incidents tabs — Alerts page simplified to just Alert Rules and History
- **Project pages simplified**: removed duplicate log viewer (937 LOC deleted), added "View Logs" button that navigates to global search with project pre-filtered
- **Settings restructured**: sub-navigation with General, Security & Data, Notifications, Team, Administration sections
- **Command palette updated**: all 9 main pages accessible with keyboard shortcuts (`g d`, `g s`, `g t`, `g m`, etc.)

### Fixed

- **OTLP Traces Ingestion**: fixed a critical typo in trace transformation where `resource_logs` was used instead of `resource_spans`, preventing proper parsing of OTLP/JSON traces.
- **OTLP Authentication**: fixed `authPlugin` to correctly handle `/v1/otlp` routes, allowing API Key authentication without requiring a valid user session.
- **LogTide JavaScript SDKs**: updated `@logtide/core`, `@logtide/fastify`, and `@logtide/sveltekit` to version `0.6.1` for improved OTLP compatibility and TraceID/SpanID serialization.
- **Frontend Environment Loading**: fixed DSN loading in SvelteKit by using `$env/dynamic/public` and added Vite proxy for `/v1/otlp` to avoid CORS issues in development.
- **LogTide SDK patterns update**: Updated all code examples in the dashboard, empty states, and onboarding flow to use the latest patterns from the `logtide-javascript` and `logtide-sdk-python` repositories.
- Node.js examples now use `@logtide/core` with `hub.init()` and `hub.captureLog()` pattern.
- Python examples now use the `logtide` package with `LogTideClient` and `client.info()` / `client.error()` methods.
- Added correct Go OpenTelemetry examples in the Traces empty state.
- **Frontend warning cleanup**: eliminated all 46 TypeScript and Svelte compiler warnings across the codebase (26 unused imports/variables, 4 deprecated `<svelte:component>` usages, 7 a11y label warnings, 2 non-reactive bindings, and miscellaneous Svelte 5 migration issues)
- **Pagination total count**: search and incidents pages now show total count ("Showing 1 to 25 of ~1,234 logs") instead of incrementing per-page — logs use fast approximate count via EXPLAIN planner estimates (no full table scan), incidents use exact COUNT(*); stale cache entries with missing totals are automatically invalidated
- **Admin dashboard timeline gaps (ClickHouse)**: periodic drops to zero in Platform Activity chart caused by bucket key format mismatch — ClickHouse produced ISO timestamps (`2026-02-26T13:00:00.000Z`) while PostgreSQL produced text format (`2026-02-26 13:00:00+00`), preventing merge; now all bucket keys are normalized to ISO format and all 24 hourly buckets are pre-filled to eliminate gaps
- **Chart locale**: timestamps no longer hardcoded to Italian locale — charts now respect user's system language
- **Silent API errors**: search and traces pages now show error toasts when data loading fails
- **Empty states**: added "No services yet" and "No errors yet" empty states to dashboard widgets
- **Docker initialization**: database is now auto-created if it doesn't exist during startup

### Removed

- Dead code cleanup: unused `Navigation.svelte` component, duplicate log viewer in project pages, unreachable code paths

---

## [0.6.4] - 2026-02-26

### Changed

- **Batch ingestion endpoint**: `POST /api/v1/ingest` now accepts flexible payload formats for better collector compatibility (Vector, Fluent Bit, etc.)
- Standard format: `{"logs": [{...}]}` (unchanged)
- Direct array: `[{log1}, {log2}]` (Vector with `codec: json`)
- Wrapped array: `[{"logs": [{...}]}, ...]` (Vector with VRL wrapping)
- Array formats auto-normalize fields via `normalizeLogData` (auto-generates `time`, normalizes `level`, extracts `service`)

---

## [0.6.3] - 2026-02-22

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<a href="https://codecov.io/gh/logtide-dev/logtide"><img src="https://codecov.io/gh/logtide-dev/logtide/branch/main/graph/badge.svg" alt="Coverage"></a>
<a href="https://hub.docker.com/r/logtide/backend"><img src="https://img.shields.io/docker/v/logtide/backend?label=docker&logo=docker" alt="Docker"></a>
<a href="https://artifacthub.io/packages/helm/logtide/logtide"><img src="https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/logtide" alt="Artifact Hub"></a>
<img src="https://img.shields.io/badge/version-0.6.3-blue.svg" alt="Version">
<img src="https://img.shields.io/badge/version-0.7.0-blue.svg" alt="Version">
<img src="https://img.shields.io/badge/license-AGPLv3-blue.svg" alt="License">
<img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Status">
<img src="https://img.shields.io/badge/cloud-free_during_alpha-success.svg" alt="Free Cloud">
Expand Down Expand Up @@ -136,7 +136,7 @@ Total control over your data. **No build required** - uses pre-built images from

**Docker Images:** [Docker Hub](https://hub.docker.com/r/logtide/backend) | [GitHub Container Registry](https://github.com/logtide-dev/logtide/pkgs/container/logtide-backend)

> **Production:** Pin versions with `LOGTIDE_BACKEND_IMAGE=logtide/backend:0.6.3` in your `.env` file.
> **Production:** Pin versions with `LOGTIDE_BACKEND_IMAGE=logtide/backend:0.7.0` in your `.env` file.

> **ARM64 / Raspberry Pi:** LogTide images support `linux/amd64` and `linux/arm64`. For Fluent Bit on ARM64, set `FLUENT_BIT_IMAGE=cr.fluentbit.io/fluent/fluent-bit:4.2.2` in your `.env` file.

Expand Down
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logtide/root",
"version": "0.6.3",
"version": "0.7.0",
"private": true,
"description": "LogTide - Self-hosted log management platform",
"author": "LogTide Team",
Expand All @@ -12,9 +12,12 @@
"express": ">=5.2.0",
"qs": ">=6.14.2",
"devalue": ">=5.6.3",
"fast-xml-parser": ">=5.3.6",
"minimatch": ">=10.2.1",
"ajv": ">=8.18.0"
"fast-xml-parser": ">=5.3.8",
"minimatch": ">=10.2.3",
"ajv": ">=8.18.0",
"rollup": ">=4.59.0",
"svelte": ">=5.53.5",
"@sveltejs/kit": ">=2.53.3"
}
},
"scripts": {
Expand Down
34 changes: 26 additions & 8 deletions packages/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
# Build stage
FROM node:20-alpine AS builder

# Install pnpm
RUN npm install -g pnpm
# Install pnpm and build dependencies for native modules (like bcrypt)
RUN apk add --no-cache python3 make g++ && \
npm install -g pnpm

WORKDIR /app

# Copy workspace files
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* tsconfig.base.json ./
COPY packages/shared ./packages/shared
COPY packages/reservoir ./packages/reservoir
COPY packages/backend ./packages/backend

# Copy all package.json files first to satisfy pnpm workspace requirements
# and allow for better caching of the install step
COPY packages/shared/package.json ./packages/shared/
COPY packages/reservoir/package.json ./packages/reservoir/
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Now copy the rest of the source for the packages we need to build
COPY packages/shared ./packages/shared
COPY packages/reservoir ./packages/reservoir
COPY packages/backend ./packages/backend

# Build packages in order
RUN pnpm --filter '@logtide/shared' build
RUN pnpm --filter '@logtide/reservoir' build
Expand All @@ -23,8 +33,9 @@ RUN pnpm --filter '@logtide/backend' build
# Production stage
FROM node:20-alpine

# Install pnpm
RUN npm install -g pnpm
# Install pnpm and runtime dependencies (nc for entrypoint)
RUN apk add --no-cache netcat-openbsd && \
npm install -g pnpm

WORKDIR /app

Expand All @@ -33,8 +44,9 @@ COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY packages/shared/package.json ./packages/shared/
COPY packages/reservoir/package.json ./packages/reservoir/
COPY packages/backend/package.json ./packages/backend/
COPY packages/frontend/package.json ./packages/frontend/

# Install production dependencies only
# Install production dependencies
RUN pnpm install --frozen-lockfile --prod

# Copy built files
Expand All @@ -43,6 +55,12 @@ COPY --from=builder /app/packages/reservoir/dist ./packages/reservoir/dist
COPY --from=builder /app/packages/backend/dist ./packages/backend/dist
COPY --from=builder /app/packages/backend/migrations ./packages/backend/migrations

# Copy source scripts (for tsx runtime execution)
COPY --from=builder /app/packages/backend/src/scripts ./packages/backend/src/scripts
COPY --from=builder /app/packages/backend/tsconfig.json ./packages/backend/tsconfig.json
COPY --from=builder /app/packages/shared/package.json ./packages/shared/package.json
COPY --from=builder /app/packages/reservoir/package.json ./packages/reservoir/package.json

# Copy entrypoint script
COPY packages/backend/entrypoint.sh ./packages/backend/entrypoint.sh
RUN chmod +x ./packages/backend/entrypoint.sh
Expand Down
26 changes: 26 additions & 0 deletions packages/backend/ascii.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@


@@@@
@@@@@@@@ @@@@@@@@@@@@
@@@@@@@@@@@@@ @@@@ @@@@@@ @@@ @@@@ @@@@
@@@@@@@@@@@@@@@@@@ @@@@ @@@@@ @@ @@@@@@ @@@@
@@@@@@@@@@@@@@@@@@@ @@@@ @@@@@ @@@@ @@@@ @@@@
@@@@@ @@@@@@@@@@@@@@@@@ @@@@ @@@@ @@@@@ @@@@ @@@@
@@@@@@@@@@ @@@@@@@@@@@@ @@@@@ @@@@ @@@@@ @@@@@@@@@@ @@@@@@@@@ @@@@ @@@@@@@@@@ @@@@ @@@@@@@@@ @@@@ @@@@@@@@@
@@@@@@@@@@@@@@ @@@@@@@@ @@@@@@@@ @@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@ @@@@@@@@@@ @@@@ @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@ @@@@@@ @@@@@@ @@@@@ @@@@@ @@@@@@ @@@@@@ @@@@@@@ @@@@ @@@@ @@@@@ @@@@@@ @@@@@@ @@@@@
@@@@@@@@@@@@@@@@@@ @@@@@@ @@@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@@ @@@@@ @@@@@ @@@@@
@@@@@@@@@@@@@@@@@@@ @@@@@@@@@ @ @@@@@ @@@@ @@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@ @@@@ @@@@@@@@@@@@@@@@@
@@ @@@@@@@@@@@@@@@@@@ @@@@@@@ @@@@ @@@@@ @@@@ @@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@ @@@@ @@@@@@@@@@@@@@@@
@@ @@@@@@@@@@@@@@@@@@ @@@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@ @@@@ @@@@@ @@@@@ @@@@@
@@ @@@@@@@@@@@@@@@@@@@@ @@@ @@@@@ @@@@@ @@@@@@ @@@@@@ @@@@@@@ @@@@@@@@ @@@@ @@@@ @@@@@@ @@@@@@ @@@@@@ @@@@@
@@@ @@@@@@@@@@@@@@@@@ @@@@@@ @@@@@ @@@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@ @@@@ @@@@ @@@@@@@@@@@@@@ @@@@@@@@@@@@@@
@@ @@@@@@@@@@@@@@ @@ @@@@@@ @@@@@ @@@@@ @@@@@@@@ @@@@@@@ @@@@@ @@@@ @@@@ @@@@@@@@ @@@@@@@@
@@@@ @@@@@@@@@@ @@@@@ @@@@@@ @@@@@ @@@@@
@@@@@@@@@@@@@@@ @@@@@@@@@ @@@@@@ @@@@ @@@@@ @@@@@@
@@@@@@ @@@@@@@@@@@@@@ @@@@@@ @@@@@@@@@@@@@@@
@@@@@@@@@@@@@ @@@@ @@@@@@@@@
@@@@@@@@@
@@@@


48 changes: 48 additions & 0 deletions packages/backend/migrations/025_audit_log.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-- Migration 025: Audit log table
-- Append-only table for compliance audit trail
-- TimescaleDB hypertable for automatic compression and retention

CREATE TABLE IF NOT EXISTS audit_log (
time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
id UUID NOT NULL DEFAULT gen_random_uuid(),
PRIMARY KEY (time, id),

organization_id UUID,
user_id UUID,
user_email TEXT,
action TEXT NOT NULL,
category TEXT NOT NULL,
resource_type TEXT,
resource_id TEXT,
ip_address TEXT,
user_agent TEXT,
metadata JSONB,

CONSTRAINT audit_log_category_check CHECK (
category IN ('log_access', 'config_change', 'user_management', 'data_modification')
)
);

SELECT create_hypertable('audit_log', 'time',
chunk_time_interval => INTERVAL '7 days',
if_not_exists => TRUE
);

ALTER TABLE audit_log SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'organization_id',
timescaledb.compress_orderby = 'time DESC'
);

SELECT add_compression_policy('audit_log', INTERVAL '30 days', if_not_exists => TRUE);
SELECT add_retention_policy('audit_log', INTERVAL '365 days', if_not_exists => TRUE);

CREATE INDEX IF NOT EXISTS idx_audit_log_org_time
ON audit_log (organization_id, time DESC);

CREATE INDEX IF NOT EXISTS idx_audit_log_org_category
ON audit_log (organization_id, category, time DESC);

CREATE INDEX IF NOT EXISTS idx_audit_log_org_user
ON audit_log (organization_id, user_id, time DESC)
WHERE user_id IS NOT NULL;
Loading
Loading