v1.0 ยท REST API
Send logs via REST API with automatic journey tracking and intelligent batching. Base URL: https://api.tracevoyage.com
Instead of making raw API calls, create a logger class that handles authentication and batching automatically. Here's how:
from voyage_logger import VoyageLogger
# Initialize once
logger = VoyageLogger(
app_name="my-app",
environment="production"
)
# Use throughout your app
logger.info(
message="User login successful",
event_name="login_success",
event_flow_name="authentication",
app_user_id="user_123",
app_username="john_doe",
meta_data={
"ip": "192.168.1.1"
}
)
# Errors with auto-ticket
logger.critical(
message="Payment failed",
event_name="payment_error",
app_user_id="user_123",
add_ticket=True
)class VoyageLogger:
def __init__(self, app_name, environment):
# 1. Get access token
self.token = self._authenticate()
self.app_name = app_name
self.environment = environment
self._queue = []
def _authenticate(self):
response = requests.post(
"https://api.tracevoyage.com/accounts/api/v1/auth/sign-in-keys",
headers={
"x-api-key": os.getenv("VOYAGE_API_KEY"),
"x-api-secret": os.getenv("VOYAGE_API_SECRET")
}
)
return response.json()["data"][0]["access_token"]
def info(self, message, **kwargs):
# 2. Build log entry
log = {
"message": message,
"level": "info",
"app_name": self.app_name,
"environment": self.environment,
**kwargs
}
# 3. Add to queue
self._queue.append(log)
# 4. Auto-flush when batch is full
if len(self._queue) >= 100:
self._flush()
def _flush(self):
# 5. Send batch to API
requests.post(
"https://api.tracevoyage.com/voyage/api/v1/logs/batch",
headers={
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
},
json={"logs": self._queue}
)
self._queue = []Choose your preferred approach: use a wrapper class for convenience, or make direct API calls for full control.
Handles auth & batching automatically
Full control over requests
import os
import requests
import time
import threading
from typing import List, Dict, Optional
class VoyageLogger:
"""
Voyage AI Logger - Wraps REST API with simple interface
Usage:
logger = VoyageLogger(app_name="my-app", environment="production")
logger.info("User logged in", app_user_id="user_123")
"""
def __init__(
self,
app_name: str,
environment: str = "production",
batch_size: int = 100,
flush_interval: float = 5.0
):
self.app_name = app_name
self.environment = environment
self.batch_size = batch_size
self.flush_interval = flush_interval
# API configuration
self.base_url = os.getenv("VOYAGE_LOGS_URL", "https://api.tracevoyage.com")
self.api_key = os.getenv("VOYAGE_LOGS_KEY")
self.api_secret = os.getenv("VOYAGE_LOGS_SECRET")
# State
self._access_token = None
self._token_expiry = 0
self._queue: List[Dict] = []
self._lock = threading.Lock()
# Authenticate on initialization
self._authenticate()
# Start background flusher
self._start_auto_flush()
def _authenticate(self):
"""Get access token from API"""
response = requests.post(
f"{self.base_url}/accounts/api/v1/auth/sign-in-keys",
headers={
"x-api-key": self.api_key,
"x-api-secret": self.api_secret
}
)
response.raise_for_status()
data = response.json()
self._access_token = data["data"][0]["access_token"]
self._token_expiry = time.time() + 3600 # 1 hour
def _refresh_token_if_needed(self):
"""Refresh token if expiring soon (within 5 minutes)"""
if time.time() >= self._token_expiry - 300:
self._authenticate()
def _send_batch(self, logs: List[Dict]) -> bool:
"""Send batch of logs to API"""
if not logs:
return True
try:
self._refresh_token_if_needed()
response = requests.post(
f"{self.base_url}/voyage/api/v1/logs/batch",
headers={
"Authorization": f"Bearer {self._access_token}",
"Content-Type": "application/json"
},
json={"logs": logs},
timeout=10
)
return response.status_code in [200, 201]
except Exception as e:
print(f"Failed to send logs: {e}")
return False
def _flush(self):
"""Flush queued logs"""
with self._lock:
if not self._queue:
return
logs = self._queue.copy()
self._queue.clear()
self._send_batch(logs)
def _start_auto_flush(self):
"""Start background thread for auto-flushing"""
def flush_worker():
while True:
time.sleep(self.flush_interval)
self._flush()
thread = threading.Thread(target=flush_worker, daemon=True)
thread.start()
def log(self, level: str, message: str, **kwargs):
"""Base log method"""
log_entry = {
"message": message,
"level": level,
"app_name": self.app_name,
"environment": self.environment,
**kwargs
}
with self._lock:
self._queue.append(log_entry)
# Auto-flush if batch is full
if len(self._queue) >= self.batch_size:
self._flush()
def debug(self, message: str, **kwargs):
"""Log debug message"""
self.log("debug", message, **kwargs)
def info(self, message: str, **kwargs):
"""Log info message"""
self.log("info", message, **kwargs)
def warn(self, message: str, **kwargs):
"""Log warning message"""
self.log("warn", message, **kwargs)
def error(self, message: str, **kwargs):
"""Log error message"""
self.log("error", message, **kwargs)
def critical(self, message: str, **kwargs):
"""Log critical message"""
self.log("critical", message, **kwargs)
def flush(self):
"""Manually flush all queued logs"""
self._flush()
# Example Usage
if __name__ == "__main__":
# Initialize logger
logger = VoyageLogger(
app_name="my-application",
environment="production"
)
# Log events
logger.info(
message="User login successful",
event_name="login_success",
event_flow_name="authentication",
app_user_id="user_123",
app_username="john_doe",
meta_data={"ip": "192.168.1.1"}
)
# Log errors with ticket creation
logger.critical(
message="Payment gateway timeout",
event_name="payment_error",
event_flow_name="checkout",
app_user_id="user_123",
add_ticket=True,
meta_data={"gateway": "stripe", "amount": 99.99}
)
# Ensure logs are sent before exit
logger.flush()const axios = require('axios');
/**
* Voyage AI Logger - Wraps REST API with simple interface
*
* Usage:
* const logger = new VoyageLogger({ appName: 'my-app', environment: 'production' });
* logger.info('User logged in', { appUserId: 'user_123' });
*/
class VoyageLogger {
constructor({
appName,
environment = 'production',
batchSize = 100,
flushInterval = 5000
}) {
this.appName = appName;
this.environment = environment;
this.batchSize = batchSize;
this.flushInterval = flushInterval;
// API configuration
this.baseUrl = process.env.VOYAGE_LOGS_URL || 'https://api.tracevoyage.com';
this.apiKey = process.env.VOYAGE_LOGS_KEY;
this.apiSecret = process.env.VOYAGE_LOGS_SECRET;
// State
this.accessToken = null;
this.tokenExpiry = 0;
this.queue = [];
// Initialize
this.authenticate().then(() => {
this.startAutoFlush();
});
}
async authenticate() {
try {
const response = await axios.post(
`${this.baseUrl}/accounts/api/v1/auth/sign-in-keys`,
{},
{
headers: {
'x-api-key': this.apiKey,
'x-api-secret': this.apiSecret
}
}
);
this.accessToken = response.data.data[0].access_token;
this.tokenExpiry = Date.now() + 3600000; // 1 hour
} catch (error) {
console.error('Authentication failed:', error.message);
throw error;
}
}
async refreshTokenIfNeeded() {
// Refresh if expiring within 5 minutes
if (Date.now() >= this.tokenExpiry - 300000) {
await this.authenticate();
}
}
async sendBatch(logs) {
if (!logs || logs.length === 0) return true;
try {
await this.refreshTokenIfNeeded();
const response = await axios.post(
`${this.baseUrl}/voyage/api/v1/logs/batch`,
{ logs },
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
},
timeout: 10000
}
);
return [200, 201].includes(response.status);
} catch (error) {
console.error('Failed to send logs:', error.message);
return false;
}
}
async flush() {
if (this.queue.length === 0) return;
const logs = [...this.queue];
this.queue = [];
await this.sendBatch(logs);
}
startAutoFlush() {
setInterval(() => {
this.flush();
}, this.flushInterval);
}
log(level, message, params = {}) {
const logEntry = {
message,
level,
app_name: this.appName,
environment: this.environment,
...params
};
this.queue.push(logEntry);
// Auto-flush if batch is full
if (this.queue.length >= this.batchSize) {
this.flush();
}
}
debug(message, params) {
this.log('debug', message, params);
}
info(message, params) {
this.log('info', message, params);
}
warn(message, params) {
this.log('warn', message, params);
}
error(message, params) {
this.log('error', message, params);
}
critical(message, params) {
this.log('critical', message, params);
}
}
// Example Usage
async function main() {
// Initialize logger
const logger = new VoyageLogger({
appName: 'my-application',
environment: 'production'
});
// Wait for authentication
await new Promise(resolve => setTimeout(resolve, 1000));
// Log events
logger.info('User login successful', {
event_name: 'login_success',
event_flow_name: 'authentication',
app_user_id: 'user_123',
app_username: 'john_doe',
meta_data: { ip: '192.168.1.1' }
});
// Log errors with ticket creation
logger.critical('Payment gateway timeout', {
event_name: 'payment_error',
event_flow_name: 'checkout',
app_user_id: 'user_123',
add_ticket: true,
meta_data: { gateway: 'stripe', amount: 99.99 }
});
// Ensure logs are sent before exit
await logger.flush();
}
module.exports = VoyageLogger;import axios from 'axios';
type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'critical';
interface LogEntry {
message: string;
level: LogLevel;
app_name: string;
environment: string;
event_name?: string;
event_flow_name?: string;
app_user_id?: string;
app_username?: string;
app_useremail?: string;
session_id?: string;
method_name?: string;
tags?: string[];
meta_data?: Record<string, any>;
exception_info?: string;
add_ticket?: boolean;
event_timeline?: Array<{ step: string; timestamp: number }>;
}
interface VoyageLoggerConfig {
appName: string;
environment?: string;
batchSize?: number;
flushInterval?: number;
}
interface LogParams {
event_name?: string;
event_flow_name?: string;
app_user_id?: string;
app_username?: string;
app_useremail?: string;
session_id?: string;
method_name?: string;
tags?: string[];
meta_data?: Record<string, any>;
exception_info?: string;
add_ticket?: boolean;
event_timeline?: Array<{ step: string; timestamp: number }>;
}
/**
* Voyage AI Logger - Type-safe wrapper for REST API
*
* @example
* const logger = new VoyageLogger({ appName: 'my-app', environment: 'production' });
* logger.info('User logged in', { appUserId: 'user_123' });
*/
class VoyageLogger {
private appName: string;
private environment: string;
private batchSize: number;
private flushInterval: number;
private baseUrl: string;
private apiKey: string;
private apiSecret: string;
private accessToken: string | null = null;
private tokenExpiry: number = 0;
private queue: LogEntry[] = [];
private flushTimer?: NodeJS.Timeout;
constructor({
appName,
environment = 'production',
batchSize = 100,
flushInterval = 5000
}: VoyageLoggerConfig) {
this.appName = appName;
this.environment = environment;
this.batchSize = batchSize;
this.flushInterval = flushInterval;
this.baseUrl = process.env.VOYAGE_LOGS_URL || 'https://api.tracevoyage.com';
this.apiKey = process.env.VOYAGE_LOGS_KEY!;
this.apiSecret = process.env.VOYAGE_LOGS_SECRET!;
this.initialize();
}
private async initialize(): Promise<void> {
await this.authenticate();
this.startAutoFlush();
}
private async authenticate(): Promise<void> {
try {
const response = await axios.post(
`${this.baseUrl}/accounts/api/v1/auth/sign-in-keys`,
{},
{
headers: {
'x-api-key': this.apiKey,
'x-api-secret': this.apiSecret
}
}
);
this.accessToken = response.data.data[0].access_token;
this.tokenExpiry = Date.now() + 3600000;
} catch (error) {
console.error('Authentication failed:', error);
throw error;
}
}
private async refreshTokenIfNeeded(): Promise<void> {
if (Date.now() >= this.tokenExpiry - 300000) {
await this.authenticate();
}
}
private async sendBatch(logs: LogEntry[]): Promise<boolean> {
if (!logs || logs.length === 0) return true;
try {
await this.refreshTokenIfNeeded();
const response = await axios.post(
`${this.baseUrl}/voyage/api/v1/logs/batch`,
{ logs },
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json'
},
timeout: 10000
}
);
return [200, 201].includes(response.status);
} catch (error) {
console.error('Failed to send logs:', error);
return false;
}
}
async flush(): Promise<void> {
if (this.queue.length === 0) return;
const logs = [...this.queue];
this.queue = [];
await this.sendBatch(logs);
}
private startAutoFlush(): void {
this.flushTimer = setInterval(() => {
this.flush();
}, this.flushInterval);
}
private log(level: LogLevel, message: string, params: LogParams = {}): void {
const logEntry: LogEntry = {
message,
level,
app_name: this.appName,
environment: this.environment,
...params
};
this.queue.push(logEntry);
if (this.queue.length >= this.batchSize) {
this.flush();
}
}
debug(message: string, params?: LogParams): void {
this.log('debug', message, params);
}
info(message: string, params?: LogParams): void {
this.log('info', message, params);
}
warn(message: string, params?: LogParams): void {
this.log('warn', message, params);
}
error(message: string, params?: LogParams): void {
this.log('error', message, params);
}
critical(message: string, params?: LogParams): void {
this.log('critical', message, params);
}
async close(): Promise<void> {
if (this.flushTimer) {
clearInterval(this.flushTimer);
}
await this.flush();
}
}
export default VoyageLogger;/accounts/api/v1/auth/sign-in-keysGet an access token using your API credentials
curl -X POST https://api.tracevoyage.com/accounts/api/v1/auth/sign-in-keys \
-H "x-api-key: YOUR_API_KEY" \
-H "x-api-secret: YOUR_API_SECRET"{
"data": [
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600
}
]
}{
"error": "Invalid credentials",
"message": "API key or secret is incorrect",
"status": 401
}Detailed info for debugging
General informational messages
Warning, non-critical issues
Error events, recoverable
Severe errors, needs attention