Live Logs
Stream and filter logs in real-time
Live Logs
Vision captures and displays all your application logs in real-time, with powerful filtering and correlation with traces.
What are Live Logs?
The Logs page shows:
- All console output -
console.log(),console.error(), etc. - Real-time streaming - Logs appear instantly
- Trace correlation - Link logs to their requests
- Filtering - Search and filter by level, service, message
- Timestamps - Precise timing for each log
Log Levels
Vision captures all standard log levels:
console.log('Info message') // INFO
console.info('Info message') // INFO
console.warn('Warning message') // WARN
console.error('Error message') // ERROR
console.debug('Debug message') // DEBUGIn the Dashboard:
[INFO] 2024-01-20 10:30:45 Info message
[WARN] 2024-01-20 10:30:46 Warning message
[ERROR] 2024-01-20 10:30:47 Error message
[DEBUG] 2024-01-20 10:30:48 Debug messageAutomatic Log Capture
With Vision Server
Logs are automatically captured:
import { Vision } from '@getvision/server'
const app = new Vision({ service: { name: 'My API' } })
app.service('users')
.endpoint('POST', '/users', schema, async (data, c) => {
console.log('Creating user:', data.email)
const user = c.span('db.insert', {}, () => {
return db.insert(users).values(data).returning().get()
})
console.log('User created:', user.id)
return user
})Dashboard shows:
[INFO] 10:30:45 Creating user: [email protected]
[INFO] 10:30:45 User created: user_123With Vision Adapters
Same automatic capture:
import { Hono } from 'hono'
import { visionAdapter } from '@getvision/adapter-hono'
const app = new Hono()
app.use('*', visionAdapter())
app.post('/users', async (c) => {
console.log('Creating user')
// Automatically captured
return c.json({ success: true })
})Both Vision Server and Adapters automatically capture all console output.
Trace Correlation
Logs are automatically linked to their traces:
app.service('users')
.endpoint('GET', '/users/:id', schema, async ({ id }, c) => {
console.log('Fetching user:', id)
const user = c.span('db.select', {}, () => {
console.log('Executing query')
return db.select().from(users).where(eq(users.id, id)).get()
})
if (!user) {
console.error('User not found:', id)
throw new Error('User not found')
}
console.log('User found:', user.email)
return user
})In the Dashboard:
Click on a trace to see its logs:
Trace: GET /users/123 [200] 45ms
├─ Logs:
│ ├─ [INFO] Fetching user: 123
│ ├─ [INFO] Executing query
│ └─ [INFO] User found: [email protected]
└─ Spans:
└─ db.select (12ms)Click on a log to see its trace:
Log: [INFO] Fetching user: 123
└─ Trace: GET /users/123 [200] 45msFiltering Logs
By Level
[Filter: ERROR]
[ERROR] 10:30:47 Database connection failed
[ERROR] 10:31:22 User not found: 456By Search Term
[Search: "user"]
[INFO] 10:30:45 Creating user: [email protected]
[INFO] 10:30:45 User created: user_123
[ERROR] 10:31:22 User not found: 456By Context Keys
Search for logs containing specific context data, even if it's not in the message text.
[Search: "orderId=ord_123"]
[INFO] 10:30:45 Creating order
[INFO] 10:30:45 Order created successfullyBy Service
[Service: Users]
[INFO] 10:30:45 Creating user
[INFO] 10:30:46 User created
[INFO] 10:31:00 Fetching userBy Time Range
[Last 5 minutes]
[INFO] 10:30:45 Recent log 1
[INFO] 10:31:00 Recent log 2Structured Logging
Log objects for better inspection:
console.log('User created', {
userId: user.id,
email: user.email,
timestamp: new Date().toISOString()
})Dashboard shows:
[INFO] 10:30:45 User created
{
"userId": "user_123",
"email": "[email protected]",
"timestamp": "2024-01-20T10:30:45.123Z"
}Error Logging
Capture errors with stack traces:
try {
const result = riskyOperation()
} catch (error) {
console.error('Operation failed:', error)
// Stack trace automatically captured
}Dashboard shows:
[ERROR] 10:30:47 Operation failed: Error: Database timeout
Stack trace:
at riskyOperation (handler.ts:45)
at async handler (handler.ts:32)
at async dispatch (hono.ts:123)Performance Logging
Log with timing information:
app.service('users')
.endpoint('GET', '/users', schema, async (_, c) => {
const start = Date.now()
const users = c.span('db.select', {}, () => {
return db.select().from(users).all()
})
const duration = Date.now() - start
console.log(`Fetched ${users.length} users in ${duration}ms`)
return { users }
})Dashboard shows:
[INFO] 10:30:45 Fetched 150 users in 45msSmart Visuals
Vision automatically cleans up your logs to make them easier to read:
- Inline Badges - Key-value pairs like
code=200orduration=150msare automatically extracted from the message and displayed as stylish inline badges. - Deduplication - The extracted keys are removed from the text message to avoid repetition.
- Context Integration - Trace context and metadata are seamlessly integrated into these badges.
Before (Raw):
INF request completed code=200 duration=131ms path=/users/1After (Vision UI):
INF request completed [code=200] [duration=131ms] [path=/users/1]Log Context (Wide Events)
Vision follows the Wide Events philosophy: add context once, see it everywhere.
The Problem
Without wide events, you'd do this:
app.post('/orders', async (c) => {
const orderId = generateId()
const userId = c.get('userId')
console.log('Creating order', { orderId, userId }) // Add context manually
await validateOrder(data)
console.log('Order validated', { orderId, userId }) // Repeat context
await chargePayment(data)
console.log('Payment charged', { orderId, userId }) // Repeat again
await createOrder(data)
console.log('Order created', { orderId, userId }) // And again...
})The Solution: c.addContext()
Add context once at the start. All logs in that request automatically include it:
// Vision Server - c.addContext() is built in
app.service('orders')
.endpoint('POST', '/orders', schema, async (data, c) => {
const orderId = generateId()
// Add context ONCE - built into context!
c.addContext({
orderId,
userId: data.userId,
'order.type': 'subscription'
})
console.log('Creating order') // Has orderId, userId automatically
await validateOrder(data)
console.log('Order validated') // Has orderId, userId automatically
await chargePayment(data)
console.log('Payment charged') // Has orderId, userId automatically
await createOrder(data)
console.log('Order created') // Has orderId, userId automatically
return order
})With Adapters (Hono/Express/Fastify), use getVisionContext():
import { getVisionContext } from '@getvision/adapter-hono'
app.post('/orders', async (c) => {
const { vision } = getVisionContext()
vision.addContext({ orderId, userId })
// ...
})Two Places to See Your Logs
1. Logs Tab (Global Search)
The Logs tab shows ALL logs from your application. You can search by:
- Log message text
- Log level (error, warn, info, debug)
- Any context field - search
userId=user_123to find all logs for that user
[Search: "orderId=ord_456"]
Results:
10:30:45 Creating order [orderId=ord_456] [userId=user_123]
10:30:46 Order validated [orderId=ord_456] [userId=user_123]
10:30:47 Payment charged [orderId=ord_456] [userId=user_123]
10:30:48 Order created [orderId=ord_456] [userId=user_123]2. Trace Details (Request-Scoped)
When you click on a trace, you see logs only from that specific request:
Trace: POST /orders [201] 320ms
Request Body:
{ "items": [...], "total": 99.99 }
Logs:
10:30:45 Creating order [orderId=ord_456]
10:30:46 Order validated [orderId=ord_456]
10:30:47 Payment charged [orderId=ord_456]
10:30:48 Order created [orderId=ord_456]
Spans:
├── validate.order (15ms)
├── payment.charge (180ms)
└── db.insert.orders (45ms)This is the power of wide events: You see the complete picture of what happened in ONE request - the HTTP data, the logs, the spans, all correlated together.
What to Add to Context
vision.addContext({
// User info
'user.id': user.id,
'user.email': user.email,
'user.plan': user.plan,
// Request info
'request.id': requestId,
'request.type': 'api',
// Business context
orderId: order.id,
'cart.items': cart.items.length,
// Feature flags
'feature.newCheckout': isEnabled('new-checkout'),
// Environment
'env': process.env.NODE_ENV
})When to Use c.addContext()
// Vision Server - add context in middleware
app.use('*', async (c, next) => {
// Add user context from auth
const user = c.get('user')
if (user) {
c.addContext({
'user.id': user.id,
'user.plan': user.plan
})
}
// Add request ID
c.addContext({
'request.id': c.req.header('X-Request-ID') || nanoid()
})
await next()
})
// Add business context in handler
app.service('orders')
.endpoint('POST', '/orders', schema, async (data, c) => {
const orderId = generateId()
c.addContext({ orderId })
// Now all logs have orderId
console.log('Processing order')
return order
})Log Retention
Configure how many logs to keep:
const app = new Vision({
service: { name: 'My API' },
vision: {
maxLogs: 10000 // Keep last 10,000 logs (default)
}
})When the limit is reached, oldest logs are removed automatically.
Best Practices
1. Use Appropriate Log Levels
// ✅ Good - Right level for the message
console.log('User logged in') // INFO
console.warn('Rate limit approaching') // WARN
console.error('Payment failed') // ERROR
console.debug('Cache hit') // DEBUG
// ❌ Bad - Wrong levels
console.error('User logged in') // Too severe
console.log('Payment failed') // Too casual2. Add Context
// ✅ Good - Rich context
console.log('Order created:', {
orderId: order.id,
userId: order.userId,
total: order.total,
items: order.items.length
})
// ❌ Bad - No context
console.log('Order created')3. Don't Log Sensitive Data
// ✅ Good - Safe logging
console.log('User authenticated:', { userId: user.id })
// ❌ Bad - Leaking secrets
console.log('User authenticated:', {
userId: user.id,
password: user.password, // Never log passwords!
apiKey: user.apiKey // Never log API keys!
})4. Use Structured Logging
// ✅ Good - Structured
console.log('Payment processed', {
amount: 99.99,
currency: 'USD',
provider: 'stripe'
})
// ❌ Bad - String concatenation
console.log('Payment processed: $99.99 USD via stripe')5. Log Important Events
// ✅ Good - Track important events
console.log('User registered:', user.email)
console.log('Order placed:', order.id)
console.error('Payment failed:', error.message)
// ❌ Bad - Too verbose
console.log('Variable x assigned')
console.log('If statement executed')
console.log('Loop iteration 1')Debugging Workflow
- See error in logs - Filter by ERROR level
- Find related trace - Click log to see trace
- Inspect request - View request body, headers
- Check spans - See which operation failed
- Review timeline - Understand what happened when
Export Logs
Coming soon: Export logs as JSON, CSV, or text files
Future features:
- Export to file - Download logs for offline analysis
- Log aggregation - Group similar logs
- Alerting - Get notified of errors
- Log forwarding - Send to external services
Next Steps
- Request Tracing - Correlate logs with traces
- API Explorer - Test and debug endpoints
- Services - View your service catalog