Skip to content

Error Handling ​

This guide covers error handling and troubleshooting for the Logstyx API.

Understanding Logstyx Error Handling ​

Asynchronous Processing Model ​

Logstyx uses an asynchronous architecture where:

  1. Gateway Layer: Accepts HTTP requests and performs basic validation
  2. Message Queue: Queues logs for reliable processing
  3. Consumer Layer: Processes logs asynchronously and validates authorization

This means most validation and authorization errors occur after the HTTP response is sent.

HTTP Responses ​

All successful HTTP requests return 200 OK with the following response:

json
{
  "status": "queued"
}

This indicates the log has been queued for processing, not that it has been successfully stored.

Why This Design? ​

This architecture provides several benefits:

  • High availability: System can accept logs even during backend processing issues
  • Better performance: Clients don't wait for full processing
  • Resilience: Logs are queued and won't be lost during temporary failures
  • Prevents retry storms: Clients don't retry on authorization failures

The Trade-off ​

The downside is that you cannot immediately verify if your log was successfully processed. You must check your Logstyx dashboard to confirm logs are being stored.


HTTP Error Responses ​

While most requests return 200 OK, you will receive error status codes for infrastructure-level issues.

Rate Limiting Errors ​

429 Too Many Requests ​

Cause: Exceeded the rate limit of 20 requests per second per IP address.

Response Example:

HTTP/1.1 429 Too Many Requests

Solution:

javascript
// Implement exponential backoff
async function sendLogWithRetry(logData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await sendLog(logData);
      return response;
    } catch (error) {
      if (error.response?.status === 429) {
        const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
        console.log(`Rate limited. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Max retries exceeded');
}

Best Practices:

  • Implement client-side batching to reduce request frequency
  • Add exponential backoff for retries
  • Monitor your request rate and adjust accordingly
  • Consider using a queue on the client side

503 Service Unavailable ​

Cause: Request burst exceeded the configured limit (50 requests).

Response Example:

HTTP/1.1 503 Service Unavailable

Solution: Same as 429 - implement exponential backoff and reduce request frequency.


Validation Errors ​

These errors occur during asynchronous processing and will not return an HTTP error. The request returns 200 OK, but the log is rejected during processing.

INVALID_INPUT Error ​

When it occurs:

  • Missing required fields (level, projectId, data)
  • Invalid level value (not one of: info, warning, error, critical)
  • Malformed JSON in request body
  • Invalid data types

Common Causes:

  1. Missing required fields:
javascript
// ❌ Wrong - missing 'data' field
{
  "level": "info",
  "projectId": "proj_123"
}

// ✅ Correct
{
  "level": "info",
  "projectId": "proj_123",
  "data": {
    "message": "User logged in"
  }
}
  1. Invalid log level:
javascript
// ❌ Wrong - invalid level
{
  "level": "debug",  // Not a valid level
  "projectId": "proj_123",
  "data": { "message": "Debug info" }
}

// ✅ Correct
{
  "level": "info",  // Valid: info, warning, error, critical
  "projectId": "proj_123",
  "data": { "message": "Debug info" }
}

How to detect: Check your Logstyx dashboard. If logs aren't appearing, validation may be failing.

Prevention:

javascript
// Validate before sending
function validateLogData(logData) {
  const validLevels = ['info', 'warning', 'error', 'critical'];
  
  if (!logData.level || !validLevels.includes(logData.level)) {
    throw new Error('Invalid log level');
  }
  
  if (!logData.projectId || typeof logData.projectId !== 'string') {
    throw new Error('Invalid project ID');
  }
  
  if (!logData.data || typeof logData.data !== 'object') {
    throw new Error('Invalid data field');
  }
  
  return true;
}

// Use before sending
try {
  validateLogData(myLog);
  await sendLog(myLog);
} catch (error) {
  console.error('Log validation failed:', error.message);
}

Authorization Errors ​

NO_ACCESS Error ​

When it occurs:

  • Origin not in allowed origins list (browser clients)
  • App ID not in allowed app IDs list (mobile clients)
  • Signature validation failed (server clients)
  • Invalid project ID

Common Causes:

1. Browser Client - Invalid Origin ​

javascript
// If your app runs on https://myapp.com
// but allowed origins only include https://app.myapp.com

// ❌ Request will fail
fetch('https://logstyx.example.com/api/v1/logs', {
  method: 'POST',
  headers: { 
    'Content-Type': 'application/json',
    // Browser automatically sets Origin: https://myapp.com
  },
  body: JSON.stringify(logData)
});

Solution: Add the origin to your project's allowed origins list in the Logstyx dashboard:

json
{
  "settings": {
    "allowedOrigin": [
      "https://myapp.com",
      "https://app.myapp.com",
      "http://localhost:3000"  // for development
    ]
  }
}

2. Mobile Client - Invalid App ID ​

javascript
// ❌ Wrong - app ID not in allowed list
{
  "level": "info",
  "projectId": "proj_123",
  "appid": "com.mycompany.wrong-app",  // Not in allowed list
  "data": { "message": "App started" }
}

// ✅ Correct - app ID in allowed list
{
  "level": "info",
  "projectId": "proj_123",
  "appid": "com.mycompany.myapp.ios",  // In allowed list
  "data": { "message": "App started" }
}

Solution: Add the app ID to allowed origins in your project settings.

3. Server Client - Signature Validation Failed ​

Common signature errors:

  1. Wrong field order:
javascript
// ❌ Wrong order
const payload = {
  projectId: body.projectId,  // Wrong order
  level: body.level,
  data: body.data,
  device: body.device,
  context: body.context
};

// ✅ Correct order
const payload = {
  level: body.level,
  projectId: body.projectId,
  device: body.device,
  context: body.context,
  data: body.data
};
  1. Signature not uppercase:
javascript
// ❌ Wrong
const signature = crypto
  .createHmac('SHA256', secret)
  .update(hashString)
  .digest('hex');  // Lowercase

// ✅ Correct
const signature = crypto
  .createHmac('SHA256', secret)
  .update(hashString)
  .digest('hex')
  .toUpperCase();  // Uppercase
  1. Timestamp not converted to integer:
javascript
// ❌ Wrong
const timestamp = Date.now();
const hashString = projectId + params + timestamp;  // timestamp is float

// ✅ Correct
const timestamp = Date.now();
const hashString = projectId + params + Math.floor(timestamp);  // Convert to int
  1. Extra spaces in JSON:
javascript
// ❌ Wrong - JSON with spaces
const params = JSON.stringify(payload, null, 2);  // Formatted JSON

// ✅ Correct - compact JSON
const params = JSON.stringify(payload);  // No extra spaces
  1. Wrong secret:
javascript
// Make sure you're using the correct project secret
const projectSecret = process.env.LOGSTYX_PROJECT_SECRET;  // From environment

Debug signature generation:

javascript
function debugSignature(projectId, projectSecret, body, timestamp) {
  const payload = {
    level: body.level,
    projectId: body.projectId,
    device: body.device,
    context: body.context,
    data: body.data
  };
  
  const params = JSON.stringify(payload);
  const hashString = projectId + params + Math.floor(timestamp);
  
  console.log('Debug Signature Generation:');
  console.log('Project ID:', projectId);
  console.log('Payload:', payload);
  console.log('Params JSON:', params);
  console.log('Timestamp:', timestamp);
  console.log('Timestamp (int):', Math.floor(timestamp));
  console.log('Hash String:', hashString);
  
  const signature = crypto
    .createHmac('SHA256', projectSecret)
    .update(hashString)
    .digest('hex')
    .toUpperCase();
  
  console.log('Generated Signature:', signature);
  
  return signature;
}

Network Errors ​

Connection Errors ​

Causes:

  • Network connectivity issues
  • Incorrect endpoint URL
  • SSL/TLS certificate problems
  • Firewall blocking requests

Example:

javascript
try {
  await sendLog(logData);
} catch (error) {
  if (error.code === 'ECONNREFUSED') {
    console.error('Cannot connect to Logstyx server');
    console.error('Check if the server is running and endpoint is correct');
  } else if (error.code === 'ENOTFOUND') {
    console.error('DNS lookup failed - check your endpoint URL');
  } else if (error.code === 'CERT_HAS_EXPIRED') {
    console.error('SSL certificate expired');
  }
}

Solutions:

  1. Verify endpoint URL is correct
  2. Check network connectivity
  3. Ensure SSL certificates are valid
  4. Check firewall rules

Timeout Errors ​

Causes:

  • Slow network connection
  • Server overload
  • Request timeout set too low

Solution:

javascript
// Increase timeout for slow networks
const axios = require('axios');

const response = await axios.post(
  'https://your-domain.com/api/v1/logs',
  logData,
  {
    timeout: 10000,  // 10 second timeout
    headers: {
      'Content-Type': 'application/json'
    }
  }
);

Monitoring and Debugging ​

Check if Logs are Being Received ​

Since the API returns 200 OK even for invalid logs, you must monitor your Logstyx dashboard:

  1. Dashboard Check: Log into your Logstyx dashboard and verify logs appear
  2. Test Log: Send a known test log and verify it appears
  3. Timeline: Check the timestamp of recent logs to ensure they're current

Test Authentication ​

Browser Client Test ​

html
<!DOCTYPE html>
<html>
<head>
  <title>Logstyx Test</title>
</head>
<body>
  <button onclick="testLog()">Send Test Log</button>
  <div id="result"></div>
  
  <script>
    async function testLog() {
      try {
        const response = await fetch('https://your-domain.com/api/v1/logs', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            level: 'info',
            projectId: 'your_project_id',
            data: {
              message: 'Test log from browser',
              testId: Date.now()
            }
          })
        });
        
        const data = await response.json();
        document.getElementById('result').innerHTML = 
          `Status: ${response.status}<br>Response: ${JSON.stringify(data)}`;
        
        alert('Log sent! Check your Logstyx dashboard in a few seconds.');
      } catch (error) {
        document.getElementById('result').innerHTML = 
          `Error: ${error.message}`;
      }
    }
  </script>
</body>
</html>

Mobile Client Test ​

javascript
// React Native example
import { useLogstyx } from 'logstyx-js-react-native';

function TestLogging() {
  const logger = useLogstyx({
    projectId: 'your_project_id',
    appid: 'com.yourcompany.app',
    endpoint: 'https://your-domain.com/api/v1/logs'
  });
  
  const testLog = async () => {
    await logger.info({
      message: 'Test log from mobile',
      testId: Date.now()
    });
    
    alert('Log sent! Check your Logstyx dashboard in a few seconds.');
  };
  
  return (
    <Button title="Test Logging" onPress={testLog} />
  );
}

Server Client Test ​

javascript
const crypto = require('crypto');
const axios = require('axios');

async function testServerLogging() {
  const projectId = 'your_project_id';
  const projectSecret = 'your_project_secret';
  const timestamp = Date.now();
  
  const body = {
    level: 'info',
    projectId: projectId,
    device: 'test-server',
    context: { test: true },
    data: {
      message: 'Test log from server',
      testId: timestamp
    }
  };
  
  // Generate signature
  const payload = {
    level: body.level,
    projectId: body.projectId,
    device: body.device,
    context: body.context,
    data: body.data
  };
  
  const params = JSON.stringify(payload);
  const hashString = projectId + params + Math.floor(timestamp);
  const signature = crypto
    .createHmac('SHA256', projectSecret)
    .update(hashString)
    .digest('hex')
    .toUpperCase();
  
  console.log('Test Log Details:');
  console.log('Timestamp:', timestamp);
  console.log('Signature:', signature);
  console.log('Body:', JSON.stringify(body, null, 2));
  
  try {
    const response = await axios.post(
      'https://your-domain.com/api/v1/logs',
      body,
      {
        headers: {
          'Content-Type': 'application/json',
          'timestamp': timestamp.toString(),
          'signature': signature
        }
      }
    );
    
    console.log('Response:', response.data);
    console.log('✅ Log sent successfully! Check your dashboard.');
  } catch (error) {
    console.error('❌ Error:', error.message);
    if (error.response) {
      console.error('Status:', error.response.status);
      console.error('Data:', error.response.data);
    }
  }
}

testServerLogging();

Common Issues and Solutions ​

Issue 1: Logs not appearing in dashboard ​

Symptoms: API returns 200 OK, but logs don't appear in dashboard.

Possible Causes:

  1. Authentication failure
  2. Invalid request format
  3. Asynchronous processing delay

Troubleshooting Steps:

  1. Wait a moment: Processing is asynchronous; wait 5-10 seconds
  2. Check authentication:
    • Browser: Verify origin is in allowed list
    • Mobile: Verify app ID is in allowed list
    • Server: Verify signature generation is correct
  3. Check project ID: Ensure you're using the correct project ID
  4. Validate request format: Ensure all required fields are present
  5. Check log level: Must be info, warning, error, or critical

Issue 2: Intermittent 429 errors ​

Symptoms: Occasional 429 Too Many Requests responses.

Cause: Request rate exceeds 20 per second per IP.

Solution:

javascript
// Implement a rate limiter
class RateLimitedLogger {
  constructor(maxRequestsPerSecond = 15) {
    this.maxRPS = maxRequestsPerSecond;
    this.queue = [];
    this.processing = false;
    
    setInterval(() => this.processQueue(), 1000);
  }
  
  async log(logData) {
    return new Promise((resolve, reject) => {
      this.queue.push({ logData, resolve, reject });
      if (!this.processing) {
        this.processQueue();
      }
    });
  }
  
  async processQueue() {
    this.processing = true;
    const batch = this.queue.splice(0, this.maxRPS);
    
    await Promise.all(
      batch.map(async ({ logData, resolve, reject }) => {
        try {
          const result = await sendLog(logData);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      })
    );
    
    this.processing = false;
  }
}

const logger = new RateLimitedLogger();

Issue 3: CORS errors (browser only) ​

Symptoms:

Access to fetch at 'https://your-domain.com/api/v1/logs' from origin 
'https://yourapp.com' has been blocked by CORS policy

Cause: Origin not in allowed origins list or CORS not configured on server.

Solution:

  1. Add origin to allowed origins in project settings
  2. Ensure Logstyx server CORS is configured correctly
  3. For development, add http://localhost:3000 to allowed origins

Issue 4: Signature always fails (server clients) ​

Debug checklist:

  • [ ] Field order: level, projectId, device, context, data
  • [ ] Signature is uppercase
  • [ ] Timestamp converted to integer with Math.floor()
  • [ ] JSON has no extra spaces
  • [ ] Using correct project secret
  • [ ] Both timestamp and signature headers are set

Test with curl:

bash
# Use the debug script from "Server Client Test" section
# to print all values, then manually construct curl command

Best Practices for Error Handling ​

1. Implement Retry Logic ​

javascript
async function sendLogWithRetry(logData, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      await sendLog(logData);
      return;
    } catch (error) {
      const isRateLimit = error.response?.status === 429 || 
                          error.response?.status === 503;
      const isLastAttempt = attempt === maxRetries - 1;
      
      if (isRateLimit && !isLastAttempt) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

2. Log Errors Locally ​

javascript
async function safeSendLog(logData) {
  try {
    await sendLog(logData);
  } catch (error) {
    // Fallback: log to console in development
    if (process.env.NODE_ENV === 'development') {
      console.log('Failed to send log:', logData);
      console.error('Error:', error.message);
    }
    
    // Could also write to local file, local storage, etc.
  }
}

3. Validate Before Sending ​

javascript
function validateAndSendLog(logData) {
  const errors = [];
  
  if (!['info', 'warning', 'error', 'critical'].includes(logData.level)) {
    errors.push('Invalid log level');
  }
  
  if (!logData.projectId) {
    errors.push('Missing project ID');
  }
  
  if (!logData.data || typeof logData.data !== 'object') {
    errors.push('Invalid data field');
  }
  
  if (errors.length > 0) {
    throw new Error(`Log validation failed: ${errors.join(', ')}`);
  }
  
  return sendLog(logData);
}

4. Monitor Success Rate ​

javascript
class LoggerWithMetrics {
  constructor() {
    this.sent = 0;
    this.failed = 0;
  }
  
  async send(logData) {
    try {
      await sendLog(logData);
      this.sent++;
    } catch (error) {
      this.failed++;
      throw error;
    }
  }
  
  getStats() {
    const total = this.sent + this.failed;
    const successRate = total > 0 ? (this.sent / total) * 100 : 0;
    return { sent: this.sent, failed: this.failed, successRate };
  }
}

Getting Help ​

If you're still experiencing issues:

  1. GitHub Issues: Report bugs
  2. GitHub Discussions: Ask questions
  3. Documentation: Review main docs

When reporting issues, include:

  • Client type (browser/mobile/server)
  • Request/response examples
  • Any error messages
  • Steps to reproduce

Released under the MIT License.