Set up HTTP / HTTPS API as an ingest method
Step 1 - Create an HTTP API ingest token
Why? Ingest tokens authenticate and authorize HTTP API requests to send data to your repository. They control which parsers can be used and what fields can be populated from the API requests.
Detailed steps:
Sign in to Falcon LogScale, and browse to your repository.
Click Settings, Ingest Tokens.
Click Add token.
Type in a descriptive name (for example, http-api-application-logs).
Set the appropriate permissions:
Assign parser to allow the API client to specify parsers in requests
Assign fields to enable field creation from API request data
Click Create token to save the token and securely store the generated string - you'll need this in the Authorization header of your HTTP requests.
Note your LogScale API endpoints:
For structured data (JSON):
Cloud: https://cloud.humio.com/api/v1/ingest/humio-structured
On-premises: https://your-logscale-host/api/v1/ingest/humio-structured
For unstructured data (raw text):
Cloud: https://cloud.humio.com/api/v1/ingest/humio-unstructured
On-premises: https://your-logscale-host/api/v1/ingest/humio-unstructured
For raw data (single event):
Cloud: https://cloud.humio.com/api/v1/ingest/raw
On-premises: https://your-logscale-host/api/v1/ingest/raw
Step 2 - Plan your HTTP API integration
Why? Planning your integration strategy ensures you choose the right API endpoint, data format, and implementation approach for your use case. Understanding the different API options and best practices helps you design an efficient, reliable ingestion pipeline.
Detailed steps:
Choose the appropriate API endpoint for your data format:
Structured endpoint (/humio-structured):
Best for: JSON-formatted events with known structure
Advantages: Automatic field extraction, batch support, efficient parsing
Use cases: Application logs, structured events, metrics, API responses
Format: Array of event objects with attributes
Unstructured endpoint (/humio-unstructured):
Best for: Plain text logs, syslog-style messages, legacy formats
Advantages: Simple format, no JSON encoding required, batch support
Use cases: Text logs, syslog messages, simple event streams
Format: Array of message objects with fields and messages
Raw endpoint (/raw):
Best for: Single events, simple integrations, testing
Advantages: Simplest format, minimal overhead
Use cases: Webhooks, single event submissions, quick testing
Format: Plain text or JSON in request body
Identify your integration pattern:
Direct application integration:
Applications send logs directly to LogScale API
Best for: Cloud-native applications, microservices, serverless functions
Considerations: Network reliability, retry logic, buffering
Script-based integration:
Scripts periodically send data to LogScale API
Best for: Batch processing, scheduled jobs, data migration
Considerations: Scheduling, error handling, state management
Webhook integration:
External services send events via webhooks to LogScale
Best for: SaaS integrations, event notifications, alerts
Considerations: Webhook format transformation, authentication
Proxy/gateway integration:
Intermediate service receives logs and forwards to LogScale
Best for: Protocol translation, aggregation, filtering
Considerations: Proxy reliability, buffering, transformation logic
Plan for batching and performance:
Determine optimal batch size (recommended: 100-1000 events per request)
Plan batch timeout (maximum time to wait before sending partial batch)
Consider compression for large payloads (gzip supported)
Estimate request rate and plan for rate limiting
Plan for reliability and error handling:
Implement retry logic with exponential backoff
Plan for local buffering during network outages
Define error handling strategy (log, alert, dead letter queue)
Consider idempotency for duplicate prevention
Plan for security:
Secure storage of ingest tokens (environment variables, secrets management)
Use HTTPS for all API requests
Implement certificate validation
Consider network security (firewall rules, VPN, private endpoints)
Assess data volumes and costs:
Estimate daily event volume and data size
Calculate network bandwidth requirements
Consider compression to reduce data transfer
Plan for burst traffic and peak loads
Step 3 - Implement basic HTTP API integration
Why? Implementing a basic integration establishes the foundation for sending data to LogScale. Starting with simple examples helps you understand the API structure and verify connectivity before adding advanced features.
Detailed steps:
Send a simple test event using curl:
Using the structured endpoint:
curl -X POST "https://cloud.humio.com/api/v1/ingest/humio-structured" \ -H "Authorization: Bearer YOUR_INGEST_TOKEN_HERE" \ -H "Content-Type: application/json" \ -d '[ { "tags": { "host": "webserver-01", "service": "api" }, "events": [ { "timestamp": "2024-01-15T10:30:00.000Z", "attributes": { "message": "Test log entry from HTTP API", "level": "INFO", "user": "test-user" } } ] } ]'Using the unstructured endpoint:
curl -X POST "https://cloud.humio.com/api/v1/ingest/humio-unstructured" \ -H "Authorization: Bearer YOUR_INGEST_TOKEN_HERE" \ -H "Content-Type: application/json" \ -d '[ { "fields": { "host": "webserver-01", "service": "api" }, "messages": [ "2024-01-15T10:30:00.000Z INFO Test log entry from HTTP API" ] } ]'Using the raw endpoint:
curl -X POST "https://cloud.humio.com/api/v1/ingest/raw" \ -H "Authorization: Bearer YOUR_INGEST_TOKEN_HERE" \ -H "Content-Type: text/plain" \ -d "Test log entry from HTTP API"
Verify the test event in LogScale:
Navigate to your repository in LogScale
Search for the test message
Verify fields are extracted correctly
Check timestamp is accurate
Implement basic integration in Python:
import requests import json from datetime import datetime # Configuration LOGSCALE_URL = "https://cloud.humio.com/api/v1/ingest/humio-structured" INGEST_TOKEN = "YOUR_INGEST_TOKEN_HERE" def send_to_logscale(events, tags=None): """ Send events to LogScale via HTTP API. Args: events (list): List of event dictionaries tags (dict): Optional tags to apply to all events Returns: bool: True if successful, False otherwise """ headers = { "Authorization": f"Bearer {INGEST_TOKEN}", "Content-Type": "application/json" } payload = [ { "tags": tags or {}, "events": events } ] try: response = requests.post( LOGSCALE_URL, headers=headers, json=payload, timeout=30 ) response.raise_for_status() print(f"Successfully sent {len(events)} events to LogScale") return True except requests.exceptions.RequestException as e: print(f"Error sending to LogScale: {str(e)}") return False # Example usage events = [ { "timestamp": datetime.utcnow().isoformat() + "Z", "attributes": { "message": "Application started", "level": "INFO", "component": "main" } }, { "timestamp": datetime.utcnow().isoformat() + "Z", "attributes": { "message": "Database connection established", "level": "INFO", "component": "database" } } ] tags = { "host": "app-server-01", "environment": "production" } send_to_logscale(events, tags)Implement basic integration in JavaScript/Node.js:
const axios = require('axios'); // Configuration const LOGSCALE_URL = 'https://cloud.humio.com/api/v1/ingest/humio-structured'; const INGEST_TOKEN = 'YOUR_INGEST_TOKEN_HERE'; async function sendToLogScale(events, tags = {}) { /** * Send events to LogScale via HTTP API. * * @param {Array} events - Array of event objects * @param {Object} tags - Optional tags to apply to all events * @returns {Promise<boolean>} - True if successful */ const headers = { 'Authorization': `Bearer ${INGEST_TOKEN}`, 'Content-Type': 'application/json' }; const payload = [ { tags: tags, events: events } ]; try { const response = await axios.post(LOGSCALE_URL, payload, { headers: headers, timeout: 30000 }); console.log(`Successfully sent ${events.length} events to LogScale`); return true; } catch (error) { console.error('Error sending to LogScale:', error.message); return false; } } // Example usage const events = [ { timestamp: new Date().toISOString(), attributes: { message: 'Application started', level: 'INFO', component: 'main' } }, { timestamp: new Date().toISOString(), attributes: { message: 'Database connection established', level: 'INFO', component: 'database' } } ]; const tags = { host: 'app-server-01', environment: 'production' }; sendToLogScale(events, tags);Implement basic integration in Java:
import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; import java.time.Instant; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonArray; public class LogScaleClient { private static final String LOGSCALE_URL = "https://cloud.humio.com/api/v1/ingest/humio-structured"; private static final String INGEST_TOKEN = "YOUR_INGEST_TOKEN_HERE"; private final HttpClient httpClient; private final Gson gson; public LogScaleClient() { this.httpClient = HttpClient.newHttpClient(); this.gson = new Gson(); } public boolean sendToLogScale(JsonArray events, JsonObject tags) { /** * Send events to LogScale via HTTP API. * * @param events Array of event objects * @param tags Optional tags to apply to all events * @return true if successful, false otherwise */ try { JsonObject payload = new JsonObject(); payload.add("tags", tags); payload.add("events", events); JsonArray payloadArray = new JsonArray(); payloadArray.add(payload); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(LOGSCALE_URL)) .header("Authorization", "Bearer " + INGEST_TOKEN) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(payloadArray))) .build(); HttpResponse<String> response = httpClient.send( request, HttpResponse.BodyHandlers.ofString() ); if (response.statusCode() == 200) { System.out.println("Successfully sent events to LogScale"); return true; } else { System.err.println("Error: " + response.statusCode() + " - " + response.body()); return false; } } catch (Exception e) { System.err.println("Error sending to LogScale: " + e.getMessage()); return false; } } // Example usage public static void main(String[] args) { LogScaleClient client = new LogScaleClient(); JsonArray events = new JsonArray(); JsonObject event1 = new JsonObject(); event1.addProperty("timestamp", Instant.now().toString()); JsonObject attributes1 = new JsonObject(); attributes1.addProperty("message", "Application started"); attributes1.addProperty("level", "INFO"); event1.add("attributes", attributes1); events.add(event1); JsonObject tags = new JsonObject(); tags.addProperty("host", "app-server-01"); tags.addProperty("environment", "production"); client.sendToLogScale(events, tags); } }Implement basic integration in Go:
package main import ( "bytes" "encoding/json" "fmt" "net/http" "time" ) const ( LogScaleURL = "https://cloud.humio.com/api/v1/ingest/humio-structured" IngestToken = "YOUR_INGEST_TOKEN_HERE" ) type Event struct { Timestamp string `json:"timestamp"` Attributes map[string]interface{} `json:"attributes"` } type Payload struct { Tags map[string]string `json:"tags"` Events []Event `json:"events"` } func sendToLogScale(events []Event, tags map[string]string) error { // Create payload payload := []Payload{ { Tags: tags, Events: events, }, } // Marshal to JSON jsonData, err := json.Marshal(payload) if err != nil { return fmt.Errorf("error marshaling JSON: %w", err) } // Create HTTP request req, err := http.NewRequest("POST", LogScaleURL, bytes.NewBuffer(jsonData)) if err != nil { return fmt.Errorf("error creating request: %w", err) } req.Header.Set("Authorization", "Bearer "+IngestToken) req.Header.Set("Content-Type", "application/json") // Send request client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return fmt.Errorf("error sending request: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("unexpected status code: %d", resp.StatusCode) } fmt.Printf("Successfully sent %d events to LogScale\n", len(events)) return nil } func main() { // Example usage events := []Event{ { Timestamp: time.Now().UTC().Format(time.RFC3339), Attributes: map[string]interface{}{ "message": "Application started", "level": "INFO", "component": "main", }, }, { Timestamp: time.Now().UTC().Format(time.RFC3339), Attributes: map[string]interface{}{ "message": "Database connection established", "level": "INFO", "component": "database", }, }, } tags := map[string]string{ "host": "app-server-01", "environment": "production", } if err := sendToLogScale(events, tags); err != nil { fmt.Printf("Error: %v\n", err) } }
Step 4 - Implement advanced features
Why? Advanced features improve reliability, performance, and operational efficiency. Implementing batching, compression, retry logic, and error handling ensures your integration can handle production workloads and edge cases.
Detailed steps:
Implement Batching:
Create a batching client in Python:
import requests import json import time import threading from datetime import datetime from collections import deque class LogScaleBatchClient: """ Batching client for LogScale HTTP API. """ def __init__(self, url, token, batch_size=100, batch_timeout=5.0): """ Initialize the batching client. Args: url (str): LogScale ingestion URL token (str): Ingest token batch_size (int): Maximum events per batch batch_timeout (float): Maximum seconds to wait before flushing """ self.url = url self.token = token self.batch_size = batch_size self.batch_timeout = batch_timeout self.buffer = deque() self.lock = threading.Lock() self.last_flush = time.time() # Start background flush thread self.flush_thread = threading.Thread(target=self._flush_loop, daemon=True) self.flush_thread.start() def log(self, message, level="INFO", **kwargs): """ Add a log event to the batch. Args: message (str): Log message level (str): Log level **kwargs: Additional attributes """ event = { "timestamp": datetime.utcnow().isoformat() + "Z", "attributes": { "message": message, "level": level, **kwargs } } with self.lock: self.buffer.append(event) # Flush if batch size reached if len(self.buffer) >= self.batch_size: self._flush() def _flush(self): """ Flush the current batch to LogScale. """ if not self.buffer: return # Get events from buffer events = list(self.buffer) self.buffer.clear() self.last_flush = time.time() # Send to LogScale headers = { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" } payload = [{"events": events}] try: response = requests.post( self.url, headers=headers, json=payload, timeout=30 ) response.raise_for_status() print(f"Flushed {len(events)} events to LogScale") except Exception as e: print(f"Error flushing to LogScale: {str(e)}") # Re-add events to buffer for retry with self.lock: self.buffer.extendleft(reversed(events)) def _flush_loop(self): """ Background thread to flush based on timeout. """ while True: time.sleep(1) with self.lock: if self.buffer and (time.time() - self.last_flush) >= self.batch_timeout: self._flush() def flush(self): """ Manually flush all pending events. """ with self.lock: self._flush() def close(self): """ Flush and close the client. """ self.flush() # Example usage client = LogScaleBatchClient( url="https://cloud.humio.com/api/v1/ingest/humio-structured", token="YOUR_INGEST_TOKEN_HERE", batch_size=100, batch_timeout=5.0 ) # Log events (automatically batched) for i in range(250): client.log( message=f"Event {i}", level="INFO", iteration=i ) # Ensure all events are sent client.close()
Implement Compression:
Add gzip compression to reduce bandwidth:
import requests import json import gzip def send_to_logscale_compressed(events, tags=None): """ Send events to LogScale with gzip compression. Args: events (list): List of event dictionaries tags (dict): Optional tags Returns: bool: True if successful """ headers = { "Authorization": f"Bearer {INGEST_TOKEN}", "Content-Type": "application/json", "Content-Encoding": "gzip" } payload = [ { "tags": tags or {}, "events": events } ] # Compress payload json_data = json.dumps(payload).encode('utf-8') compressed_data = gzip.compress(json_data) print(f"Original size: {len(json_data)} bytes") print(f"Compressed size: {len(compressed_data)} bytes") print(f"Compression ratio: {len(compressed_data)/len(json_data):.2%}") try: response = requests.post( LOGSCALE_URL, headers=headers, data=compressed_data, timeout=30 ) response.raise_for_status() return True except Exception as e: print(f"Error: {str(e)}") return False
Implement Retry Logic with Exponential Backoff:
Add robust retry logic:
import requests import time from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def create_session_with_retries(): """ Create a requests session with retry logic. Returns: requests.Session: Configured session """ session = requests.Session() # Configure retry strategy retry_strategy = Retry( total=5, # Maximum number of retries backoff_factor=1, # Wait 1, 2, 4, 8, 16 seconds status_forcelist=[429, 500, 502, 503, 504], # Retry on these status codes allowed_methods=["POST"] # Retry POST requests ) adapter = HTTPAdapter(max_retries=retry_strategy) session.mount("http://", adapter) session.mount("https://", adapter) return session def send_to_logscale_with_retry(events, tags=None, max_retries=5): """ Send events to LogScale with custom retry logic. Args: events (list): List of event dictionaries tags (dict): Optional tags max_retries (int): Maximum retry attempts Returns: bool: True if successful """ headers = { "Authorization": f"Bearer {INGEST_TOKEN}", "Content-Type": "application/json" } payload = [ { "tags": tags or {}, "events": events } ] session = create_session_with_retries() for attempt in range(max_retries): try: response = session.post( LOGSCALE_URL, headers=headers, json=payload, timeout=30 ) response.raise_for_status() print(f"Successfully sent {len(events)} events") return True except requests.exceptions.HTTPError as e: if e.response.status_code == 429: # Rate limited - wait longer wait_time = 2 ** attempt * 2 print(f"Rate limited. Waiting {wait_time}s before retry {attempt + 1}/{max_retries}") time.sleep(wait_time) elif e.response.status_code >= 500: # Server error - retry with backoff wait_time = 2 ** attempt print(f"Server error. Waiting {wait_time}s before retry {attempt + 1}/{max_retries}") time.sleep(wait_time) else: # Client error - don't retry print(f"Client error: {e.response.status_code}") return False except requests.exceptions.RequestException as e: # Network error - retry with backoff wait_time = 2 ** attempt print(f"Network error. Waiting {wait_time}s before retry {attempt + 1}/{max_retries}") time.sleep(wait_time) print(f"Failed after {max_retries} attempts") return False
Implement Local Buffering for Reliability:
Add disk-based buffering for network outages:
import requests import json import os import time from pathlib import Path class BufferedLogScaleClient: """ LogScale client with disk-based buffering. """ def __init__(self, url, token, buffer_dir="/tmp/logscale_buffer"): """ Initialize buffered client. Args: url (str): LogScale ingestion URL token (str): Ingest token buffer_dir (str): Directory for buffering failed requests """ self.url = url self.token = token self.buffer_dir = Path(buffer_dir) self.buffer_dir.mkdir(parents=True, exist_ok=True) def send(self, events, tags=None): """ Send events to LogScale with buffering. Args: events (list): List of event dictionaries tags (dict): Optional tags Returns: bool: True if sent or buffered successfully """ headers = { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" } payload = [ { "tags": tags or {}, "events": events } ] try: response = requests.post( self.url, headers=headers, json=payload, timeout=30 ) response.raise_for_status() print(f"Successfully sent {len(events)} events") # Try to send buffered events self._send_buffered() return True except Exception as e: print(f"Error sending to LogScale: {str(e)}") print("Buffering events to disk") # Save to buffer buffer_file = self.buffer_dir / f"buffer_{int(time.time() * 1000)}.json" with open(buffer_file, 'w') as f: json.dump(payload, f) return True def _send_buffered(self): """ Attempt to send buffered events. """ buffer_files = sorted(self.buffer_dir.glob("buffer_*.json")) for buffer_file in buffer_files: try: with open(buffer_file, 'r') as f: payload = json.load(f) headers = { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" } response = requests.post( self.url, headers=headers, json=payload, timeout=30 ) response.raise_for_status() # Success - delete buffer file buffer_file.unlink() print(f"Sent buffered events from {buffer_file.name}") except Exception as e: print(f"Error sending buffered events: {str(e)}") # Keep buffer file for next attempt break # Example usage client = BufferedLogScaleClient( url="https://cloud.humio.com/api/v1/ingest/humio-structured", token="YOUR_INGEST_TOKEN_HERE" ) events = [ { "timestamp": "2024-01-15T10:30:00.000Z", "attributes": { "message": "Test event", "level": "INFO" } } ] client.send(events)
Implement Parser Selection:
Specify parser in API request:
def send_with_parser(events, parser_name, tags=None): """ Send events to LogScale with specific parser. Args: events (list): List of event dictionaries parser_name (str): Name of parser to use tags (dict): Optional tags Returns: bool: True if successful """ headers = { "Authorization": f"Bearer {INGEST_TOKEN}", "Content-Type": "application/json" } # Add parser to tags if tags is None: tags = {} tags["@parser"] = parser_name payload = [ { "tags": tags, "events": events } ] try: response = requests.post( LOGSCALE_URL, headers=headers, json=payload, timeout=30 ) response.raise_for_status() return True except Exception as e: print(f"Error: {str(e)}") return False # Example: Use JSON parser events = [ { "timestamp": "2024-01-15T10:30:00.000Z", "attributes": { "message": "Test event", "level": "INFO" } } ] send_with_parser(events, parser_name="json")
Implement Async/Non-blocking Sending:
Use async/await for non-blocking operations:
import asyncio import aiohttp import json from datetime import datetime class AsyncLogScaleClient: """ Async LogScale client for non-blocking operations. """ def __init__(self, url, token): """ Initialize async client. Args: url (str): LogScale ingestion URL token (str): Ingest token """ self.url = url self.token = token self.session = None async def __aenter__(self): """Context manager entry.""" self.session = aiohttp.ClientSession() return self async def __aexit__(self, exc_type, exc_val, exc_tb): """Context manager exit.""" if self.session: await self.session.close() async def send(self, events, tags=None): """ Send events to LogScale asynchronously. Args: events (list): List of event dictionaries tags (dict): Optional tags Returns: bool: True if successful """ headers = { "Authorization": f"Bearer {self.token}", "Content-Type": "application/json" } payload = [ { "tags": tags or {}, "events": events } ] try: async with self.session.post( self.url, headers=headers, json=payload, timeout=aiohttp.ClientTimeout(total=30) ) as response: response.raise_for_status() print(f"Successfully sent {len(events)} events") return True except Exception as e: print(f"Error: {str(e)}") return False # Example usage async def main(): async with AsyncLogScaleClient( url="https://cloud.humio.com/api/v1/ingest/humio-structured", token="YOUR_INGEST_TOKEN_HERE" ) as client: # Send multiple batches concurrently tasks = [] for i in range(10): events = [ { "timestamp": datetime.utcnow().isoformat() + "Z", "attributes": { "message": f"Event from batch {i}", "level": "INFO", "batch": i } } ] tasks.append(client.send(events)) # Wait for all sends to complete results = await asyncio.gather(*tasks) print(f"Sent {sum(results)} batches successfully") # Run async main asyncio.run(main())
Step 5 - Test and verify
Why? Testing confirms that your HTTP API integration is working correctly before deploying to production, ensuring proper data formatting, authentication, error handling, and performance under various conditions.
Detailed steps:
Test basic connectivity and authentication:
Send a simple test event using curl
Verify successful response (HTTP 200)
Test with invalid token to verify authentication is working
Test with malformed JSON to verify error handling
Verify data in LogScale:
Navigate to your repository in LogScale
Search for test events
Verify events have correct timestamps
Confirm all attributes are present and correctly typed
Check that tags are applied correctly
Verify parser is processing data as expected
Test batching functionality:
Send multiple events in a single batch
Verify all events appear in LogScale
Test with maximum recommended batch size (1000 events)
Verify batch timeout triggers flush correctly
Test compression:
Send compressed payload
Verify events are received correctly
Compare compressed vs. uncompressed payload sizes
Measure performance difference
Test error handling and retry logic:
Simulate network failure (disconnect network temporarily)
Verify events are buffered locally
Restore network and verify buffered events are sent
Test retry logic with temporary server errors
Verify exponential backoff is working correctly
Test performance and throughput:
Send high volume of events (10,000+)
Measure events per second throughput
Monitor response times
Check for any rate limiting or throttling
Verify all events are eventually received
Test different data formats:
Test structured endpoint with JSON events
Test unstructured endpoint with text messages
Test raw endpoint with single events
Verify each format is parsed correctly
Test edge cases:
Send events with special characters and Unicode
Send very large events (test size limits)
Send events with missing required fields
Send events with invalid timestamps
Test concurrent requests from multiple threads
Validate end-to-end latency:
Send event with unique identifier and timestamp
Measure time from send to availability in LogScale
Typical latency: 1-5 seconds for direct API calls
Test monitoring and observability:
Verify logging of successful sends
Verify logging of errors and retries
Check metrics are being collected (if implemented)
Test alerting on failures (if implemented)
Step 6 - Monitoring and maintenance
Why? Ongoing monitoring ensures the reliability, performance, and efficiency of your HTTP API integration, enabling proactive issue detection and continuous optimization of your ingestion pipeline.
What you should do:
Set up monitoring and alerts:
Monitor HTTP response codes (track 4xx and 5xx errors)
Alert on authentication failures (401 errors)
Alert on rate limiting (429 errors)
Monitor request latency and timeouts
Track successful vs. failed send attempts
Monitor buffer size and disk usage (if using local buffering)
Alert on sustained high error rates
Monitor performance metrics:
Events sent per second
Average batch size
Request latency (p50, p95, p99)
Network bandwidth usage
Compression ratio (if using compression)
Retry rate and success after retry
Monitor data quality:
Verify events are appearing in LogScale
Check for gaps in timestamps
Validate field extraction accuracy
Monitor for duplicate events
Verify tags are applied correctly
Regularly review and optimize:
Batch sizes based on event volume and latency requirements
Batch timeout settings
Retry logic and backoff parameters
Buffer sizes and flush intervals
Compression settings based on payload sizes
Connection pool settings and timeouts
Implement security best practices:
Rotate ingest tokens regularly (every 90 days recommended)
Store tokens securely (environment variables, secrets management)
Use HTTPS for all requests (never HTTP)
Implement certificate validation
Audit API access and usage patterns
Monitor for unauthorized access attempts
Implement rate limiting on client side to prevent abuse
Handle application changes:
Update client libraries when new versions are released
Test API changes in non-production environments
Update field mappings when log formats change
Adjust parsers as needed for new data structures
Document all integration changes
Plan for scaling:
Monitor event volume trends
Plan for increased throughput requirements
Consider multiple client instances for high volumes
Implement load balancing if needed
Test disaster recovery procedures
Document integration architecture
Troubleshoot common issues:
401 Unauthorized:
Verify ingest token is correct
Check Authorization header format
Ensure token has not expired or been revoked
400 Bad Request:
Validate JSON payload structure
Check timestamp format (ISO 8601 required)
Verify required fields are present
Check for invalid characters or encoding issues
413 Payload Too Large:
Reduce batch size
Enable compression
Split large events into smaller chunks
429 Too Many Requests:
Implement rate limiting on client side
Increase batch size to reduce request frequency
Implement exponential backoff
Contact support if rate limits are too restrictive
Timeout errors:
Increase timeout values
Reduce batch size
Check network connectivity
Verify LogScale service status
Missing events:
Check for errors in client logs
Verify retry logic is working
Check buffer for unsent events
Verify events meet parser requirements
Maintain operational documentation:
Document API endpoints and authentication
Maintain runbooks for common issues
Document client configuration and settings
Keep inventory of all API integrations
Document data formats and field mappings
Maintain change logs for integration updates
Conduct regular reviews:
Quarterly reviews of all API integrations
Monthly performance reviews and optimization
Weekly operational reviews of errors and warnings
Annual security audits and token rotation
Leverage community resources:
Review LogScale API documentation for updates
Participate in community forums
Explore example implementations and client libraries
Share lessons learned with the community