LogTide
Language
Easy

Deno Runtime Logging Integration

Use the LogTide JavaScript SDK with Deno via npm specifiers for structured logging with secure-by-default permissions.

npm: specifier support Secure-by-default Native TypeScript Deno Deploy compatible

Deno supports npm packages via npm: specifiers, making the LogTide JavaScript SDK work directly. This guide covers setup, Fresh framework integration, Deno.serve patterns, and Deno Deploy considerations.

Why use LogTide with Deno?

  • npm: specifier: Import @logtide/node directly — no import maps needed
  • Secure-by-default: Deno’s permission model means logging requires explicit --allow-net
  • Native TypeScript: No build step, full type checking out of the box
  • Deno Deploy: Ship logs from edge functions to your LogTide instance
  • Standard APIs: Uses Web Standard Request/Response patterns

Prerequisites

  • Deno 1.38+ (2.x recommended for improved npm compatibility)
  • LogTide instance with a DSN or API key

Quick Start

// main.ts
import { LogTideClient } from 'npm:@logtide/node';

const client = new LogTideClient({
  dsn: Deno.env.get('LOGTIDE_DSN')!,
  service: 'deno-app',
  environment: Deno.env.get('DENO_ENV') ?? 'development',
});

client.info('Application started', { runtime: 'deno', version: Deno.version.deno });

Deno.serve({ port: 8000 }, async (req) => {
  const url = new URL(req.url);
  client.info(`${req.method} ${url.pathname}`);
  return new Response('OK');
});

Run with permissions:

deno run --allow-net --allow-env main.ts

Environment Variables

Create a .env file and load it:

# .env
LOGTIDE_DSN=http://[email protected]:8080
DENO_ENV=development
deno run --allow-net --allow-env --allow-read=.env main.ts

Or with Deno 2.x, .env files load automatically with --allow-env.

Deno.serve with Structured Logging

// main.ts
import { LogTideClient } from 'npm:@logtide/node';

const client = new LogTideClient({
  dsn: Deno.env.get('LOGTIDE_DSN')!,
  service: 'api',
});

Deno.serve({ port: 8000 }, async (req) => {
  const url = new URL(req.url);
  const start = performance.now();
  const traceId = req.headers.get('x-trace-id') ?? crypto.randomUUID();

  // Skip health checks
  if (url.pathname === '/health') return new Response('OK');

  try {
    const response = await handleRequest(req, url);
    const durationMs = Math.round(performance.now() - start);

    client.info(`${req.method} ${url.pathname} ${response.status}`, {
      method: req.method,
      path: url.pathname,
      statusCode: response.status,
      durationMs,
      traceId,
    });

    response.headers.set('X-Trace-Id', traceId);
    return response;
  } catch (error) {
    client.error(`${req.method} ${url.pathname} 500`, {
      method: req.method,
      path: url.pathname,
      statusCode: 500,
      traceId,
      error: error instanceof Error ? error.message : String(error),
    });

    return new Response('Internal Server Error', { status: 500 });
  }
});

async function handleRequest(req: Request, url: URL): Promise<Response> {
  switch (url.pathname) {
    case '/api/users':
      return Response.json([]);
    default:
      return new Response('Not Found', { status: 404 });
  }
}

Fresh Framework Integration

Fresh is Deno’s full-stack framework. Add logging to middleware and routes:

Middleware

// routes/_middleware.ts
import { FreshContext } from '$fresh/server.ts';
import { LogTideClient } from 'npm:@logtide/node';

const client = new LogTideClient({
  dsn: Deno.env.get('LOGTIDE_DSN')!,
  service: 'fresh-app',
});

export async function handler(req: Request, ctx: FreshContext) {
  const url = new URL(req.url);
  const start = performance.now();
  const traceId = crypto.randomUUID();

  // Pass trace ID to downstream handlers
  ctx.state.traceId = traceId;
  ctx.state.logtide = client;

  const response = await ctx.next();
  const durationMs = Math.round(performance.now() - start);

  // Skip static assets
  if (!url.pathname.startsWith('/_frsh/')) {
    client.info(`${req.method} ${url.pathname} ${response.status}`, {
      method: req.method,
      path: url.pathname,
      statusCode: response.status,
      durationMs,
      traceId,
    });
  }

  response.headers.set('X-Trace-Id', traceId);
  return response;
}

API Routes

// routes/api/users/[id].ts
import { Handlers } from '$fresh/server.ts';

export const handler: Handlers = {
  async GET(_req, ctx) {
    const { logtide, traceId } = ctx.state;
    const { id } = ctx.params;

    logtide.info('Fetching user', { userId: id, traceId });

    const user = await db.getUser(id);
    if (!user) {
      logtide.warning('User not found', { userId: id, traceId });
      return new Response('Not Found', { status: 404 });
    }

    return Response.json(user);
  },
};

Islands (Client Components)

Islands run in the browser — don’t import server logging there. Keep logging in routes and middleware only.

Oak Framework Integration

If using Oak (Deno’s Express-like framework):

// main.ts
import { Application, Router } from 'https://deno.land/x/oak/mod.ts';
import { LogTideClient } from 'npm:@logtide/node';

const client = new LogTideClient({
  dsn: Deno.env.get('LOGTIDE_DSN')!,
  service: 'oak-api',
});

const app = new Application();

// Logging middleware
app.use(async (ctx, next) => {
  const start = performance.now();
  await next();
  const durationMs = Math.round(performance.now() - start);

  client.info(`${ctx.request.method} ${ctx.request.url.pathname} ${ctx.response.status}`, {
    method: ctx.request.method,
    path: ctx.request.url.pathname,
    statusCode: ctx.response.status,
    durationMs,
  });
});

// Error handler
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (error) {
    client.error('Unhandled error', {
      error: error instanceof Error ? error.message : String(error),
      path: ctx.request.url.pathname,
    });
    ctx.response.status = 500;
    ctx.response.body = { error: 'Internal Server Error' };
  }
});

const router = new Router();
router.get('/users/:id', (ctx) => {
  client.info('Fetching user', { userId: ctx.params.id });
  ctx.response.body = { id: ctx.params.id };
});

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });

Deno Deploy

Deno Deploy runs your code at the edge. LogTide works there with some considerations:

// main.ts (for Deno Deploy)
import { LogTideClient } from 'npm:@logtide/node';

const client = new LogTideClient({
  dsn: Deno.env.get('LOGTIDE_DSN')!,
  service: 'edge-api',
  // Smaller batches for edge (shorter-lived processes)
  batchSize: 10,
  flushInterval: 2000,
});

Deno.serve(async (req) => {
  const url = new URL(req.url);
  const start = performance.now();

  const response = await handleRequest(req, url);
  const durationMs = Math.round(performance.now() - start);

  client.info(`${req.method} ${url.pathname} ${response.status}`, {
    method: req.method,
    path: url.pathname,
    statusCode: response.status,
    durationMs,
    region: Deno.env.get('DENO_REGION') ?? 'unknown',
  });

  return response;
});

Notes for Deploy:

  • Set LOGTIDE_DSN in the Deno Deploy dashboard environment variables
  • Use smaller batch sizes since edge functions may terminate quickly
  • Network permissions are granted automatically on Deploy

Permissions Reference

Deno requires explicit permissions. For LogTide:

# Minimum permissions
deno run \
  --allow-net=your-logtide-host:8080 \  # Network to LogTide
  --allow-env=LOGTIDE_DSN,DENO_ENV \    # Environment variables
  main.ts

# Development (more permissive)
deno run --allow-net --allow-env --allow-read main.ts

Docker Deployment

FROM denoland/deno:2.0.0

WORKDIR /app

# Cache dependencies
COPY deno.json deno.lock ./
RUN deno install

# Copy application
COPY . .

# Cache the main module
RUN deno cache main.ts

ENV LOGTIDE_DSN=""

EXPOSE 8000
CMD ["deno", "run", "--allow-net", "--allow-env", "main.ts"]

Performance

MetricValue
Startup time~100ms
Log call overhead<0.3ms (batched)
Memory overhead~8MB
npm: specifier loadFirst run only (cached after)

Troubleshooting

”Requires net access”

Run with --allow-net or specify your LogTide host:

deno run --allow-net=api.logtide.dev main.ts

npm package not found

Ensure you’re using the npm: prefix:

// Correct
import { LogTideClient } from 'npm:@logtide/node';

// Wrong (won't resolve)
import { LogTideClient } from '@logtide/node';

Slow first run

Deno downloads and caches npm packages on first run. Subsequent runs are fast. In Docker, use RUN deno cache main.ts to pre-cache.

Next Steps