Build hn-reader: keyboard-driven Hacker News reader
CI / build (push) Successful in 2m0s

Vite/React + shadcn/ui frontend with Superhuman-style keyboard nav
(j/k/o/v/e/s/u, feed switching, localStorage caching for instant
loads) backed by an Express API that polls the HN Firebase API into
Postgres and serves cached feeds/comments. Instrumented with
OpenTelemetry (traces/metrics/logs via OTLP/HTTP).
This commit is contained in:
privatecloud builder
2026-07-25 12:16:08 +00:00
commit 95bef8bf7b
47 changed files with 12198 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
// OpenTelemetry bootstrap: sends traces, metrics and logs to the LGTM
// stack's OTLP/HTTP collector at http://localhost:4318. Must be required
// before any other module so auto-instrumentation can patch http/express/pg.
const { NodeSDK } = require('@opentelemetry/sdk-node')
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http')
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http')
const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http')
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics')
const { BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs')
const { resourceFromAttributes } = require('@opentelemetry/resources')
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions')
const { logs } = require('@opentelemetry/api-logs')
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'
const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: 'hn-reader-api',
[ATTR_SERVICE_VERSION]: '1.0.0',
})
const sdk = new NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ url: `${endpoint}/v1/traces` }),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: `${endpoint}/v1/metrics` }),
exportIntervalMillis: 15000,
}),
logRecordProcessors: [
new BatchLogRecordProcessor(new OTLPLogExporter({ url: `${endpoint}/v1/logs` })),
],
instrumentations: [
getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-fs': { enabled: false },
}),
],
})
sdk.start()
process.on('SIGTERM', () => sdk.shutdown().finally(() => process.exit(0)))
const otelLogger = logs.getLogger('hn-reader-api')
// Small structured logger: prints to stdout (captured by journald) and
// emits an OTLP log record so logs show up alongside traces in Grafana.
function log(severity, message, attributes = {}) {
const line = `[${severity}] ${message} ${Object.keys(attributes).length ? JSON.stringify(attributes) : ''}`
if (severity === 'ERROR') console.error(line)
else console.log(line)
otelLogger.emit({
severityText: severity,
body: message,
attributes,
})
}
module.exports = { log }