Core SDK
The logstyx-js-core package is the foundation for all JS platform-specific Logstyx SDKs. It provides the core logging functionality and handles payload construction, sanitization, and authentication.
When to Use Core SDK
Use the core SDK when:
- Building a custom integration for an unsupported framework
- Creating your own wrapper with additional functionality
- You need maximum control over the logging behavior
- Platform-specific SDKs don't fit your use case
For Most Users
If you're using Node.js, React, Express, or other supported platforms, use the platform-specific SDK instead. They provide better developer experience with framework-specific features.
Installation
npm install github:devatlogstyx/logstyx-js-core#releaseBasic Usage
const { useLogstyx } = require('logstyx-js-core');
const logger = useLogstyx({
projectId: 'your_project_id',
apiKey: 'your_api_key',
signatureFunc: (projectId, apiKey, payload) => {
// Your signature implementation
return { timestamp, signature };
},
sendFunc: (endpoint, options) => {
// Your HTTP client implementation
return fetch(endpoint, options);
}
});
// Send logs
logger.info({ message: 'Application started' });
logger.error({ message: 'Error occurred', error: err });Configuration Options
Required Options
projectId (string)
Your Logstyx project identifier. Get this from your project settings in the Logstyx dashboard.
projectId: 'proj_abc123xyz'endpoint (string)
The API endpoint to send logs to. Defaults to the public Logstyx instance.
endpoint: 'https://your-domain.com/api/v1/logs'For self-hosted instances, point this to your deployment.
Optional Options
apiKey (string)
Required for server-side authentication. Used to generate HMAC signatures.
apiKey: 'your_api_key_here'Never Expose API Keys
API keys should only be used server-side. Never include them in client-side code or commit them to version control.
appid (string)
Required for React Native applications. Typically your app's bundle identifier in reverse domain notation.
appid: 'com.yourcompany.appname'device (any)
Optional device identifier. Useful for tracking logs from specific devices or instances.
device: 'server-01' // or device UUID for mobile appssignatureFunc (function)
Function to generate authentication signatures for server-side requests. Required when using apiKey.
Signature:
(projectId: string, apiKey: string, payload: object) => {
timestamp: number,
signature: string
}Required implementation:
const crypto = require('crypto');
function signatureFunc(projectId, apiKey, payload) {
const timestamp = new Date().getTime();
let params = JSON.stringify(body);
const Hash = projectId + params + parseInt(timestamp);
const signature = crypto
.createHmac(`SHA256`, apiKey)
.update(Hash)
.digest("hex")
.toUpperCase();
return {
timestamp,
signature
}
}Important
The signature must be generated exactly as shown above to match the server's validation:
- Message format:
projectId + JSON.stringify(payload) + timestamp - Timestamp must be in milliseconds
- Signature must be uppercase
- HMAC algorithm: SHA256
Any deviation will result in authentication failures.
sendFunc (function)
Custom function to send HTTP requests. Allows you to use your preferred HTTP client.
Signature:
(endpoint: string, options: object) => Promise<Response>Default behavior:
function sendFunc(endpoint, options) {
return fetch(endpoint, options);
}Custom example with axios:
const axios = require('axios');
function sendFunc(endpoint, options) {
return axios.post(endpoint, JSON.parse(options.body), {
headers: options.headers
});
}API Methods
logger.info(data)
Send an informational log.
logger.info({
message: 'User logged in',
userId: '12345',
timestamp: Date.now()
});logger.warning(data)
Send a warning log.
logger.warning({
message: 'API rate limit approaching',
currentRate: 950,
limit: 1000
});logger.error(data)
Send an error log.
logger.error({
message: 'Database connection failed',
error: err.message,
host: 'db.example.com'
});logger.critical(data)
Send a critical log for severe issues.
logger.critical({
message: 'System failure',
service: 'payment-processor',
impact: 'All transactions blocked'
});logger.send(level, data)
Send a log with a custom level.
logger.send('info', { message: 'Custom log' });
logger.send('error', { message: 'Error log' });logger.setContext(context)
Set persistent context that will be included in all subsequent logs.
logger.setContext({
userId: '12345',
sessionId: 'sess_xyz',
environment: 'production'
});
// All logs after this will include the context
logger.info({ message: 'Action performed' });
// Sent as: { message: 'Action performed', userId: '12345', sessionId: 'sess_xyz', ... }logger.clearContext(keys?)
Clear specific context keys or all context.
// Clear specific keys
logger.clearContext(['sessionId']);
// Clear all context
logger.clearContext();Payload Structure
Generic Payload Format
The core SDK constructs payloads in the following format before sending:
{
level: 'info', // Log level
projectId: 'proj_xyz', // Your project ID
appid: 'com.app.name', // Optional: for React Native
device: 'device-id', // Optional: device identifier
context: { // Sanitized context
userId: '123',
environment: 'prod'
},
data: { // Sanitized and normalized data
message: 'Log message',
customField: 'value'
}
}Payload Sanitization
All payloads go through sanitization to remove invalid values:
Removed values:
undefinednull- Empty strings (
"") - Empty arrays (
[]) - Empty objects (
{})
Example:
// Input
logger.info({
message: 'Test',
validField: 'value',
nullField: null,
undefinedField: undefined,
emptyString: '',
emptyArray: [],
emptyObject: {}
});
// Actual payload sent
{
message: 'Test',
validField: 'value'
// All empty/null/undefined fields removed
}Data Normalization
The data field is normalized with the following rules:
Error Objects
Errors are automatically expanded to have title, message and stack
const error = new Error('Something failed');
error.statusCode = 500;
logger.error(error);
// Normalized to:
{
title: 'Error',
message: 'Something failed',
stack: '...',
statusCode: 500
}Depth Limiting
Objects are cloned up to a maximum depth of 2 levels:
logger.info({
level1: {
level2: {
level3: {
level4: 'value'
}
}
}
});
// Normalized to:
{
level1: {
level2: '[Max depth reached]'
}
}Circular Reference Protection
Circular references are detected and replaced:
const obj = { name: 'test' };
obj.self = obj; // Circular reference
logger.info(obj);
// Normalized to:
{
name: 'test',
self: '[Circular]'
}String Truncation
Long strings are truncated to 500 characters:
logger.info({
longString: 'x'.repeat(1000)
});
// Normalized to:
{
longString: 'xxx...xxx (truncated)'
}Array Limiting
Arrays are limited to the first 10 elements:
logger.info({
items: Array(100).fill('item')
});
// Normalized to:
{
items: ['item', 'item', ...] // Only first 10 items
}Private Properties
Properties starting with underscore are excluded:
logger.info({
publicField: 'visible',
_privateField: 'hidden'
});
// Normalized to:
{
publicField: 'visible'
// _privateField is excluded
}Primitive Values
Primitive values are wrapped in a message object:
logger.info('Simple string');
// Becomes: { message: 'Simple string' }
logger.info(123);
// Becomes: { message: '123' }
logger.info(['array', 'items']);
// Becomes: { message: '["array","items"]' }Authentication Flow
Server-Side (HMAC Signature)
When signatureFunc is provided:
- Payload is constructed and sanitized
signatureFuncis called withprojectId,apiKey, andpayload- Signature and timestamp are added to request headers
- Request is sent with authentication headers
// Headers sent
{
"Content-Type": "application/json",
"signature": "abc123...",
"timestamp": 1234567890
}Client-Side (Origin-Based)
For browser/React (no signatureFunc):
- Payload is constructed and sanitized
- Request is sent without signature headers
- Server validates based on allowed origins
// Headers sent
{
"Content-Type": "application/json"
}React Native (App ID)
For mobile apps with appid:
- Payload includes
appidfield - Request is sent without signature headers
- Server validates based on registered app IDs
Advanced Usage
Custom HTTP Client
Use your own HTTP client for better control:
const axios = require('axios');
const logger = useLogstyx({
projectId: 'proj_xyz',
sendFunc: async (endpoint, options) => {
try {
const response = await axios({
url: endpoint,
method: options.method,
headers: options.headers,
data: JSON.parse(options.body),
timeout: 5000
});
return response;
} catch (error) {
console.error('Failed to send log:', error);
throw error;
}
}
});Batch Logging
Implement batching by wrapping the core SDK:
class BatchLogger {
constructor(logger, batchSize = 10, flushInterval = 5000) {
this.logger = logger;
this.batch = [];
this.batchSize = batchSize;
setInterval(() => this.flush(), flushInterval);
}
log(level, data) {
this.batch.push({ level, data });
if (this.batch.length >= this.batchSize) {
this.flush();
}
}
flush() {
if (this.batch.length === 0) return;
this.batch.forEach(({ level, data }) => {
this.logger.send(level, data);
});
this.batch = [];
}
}
const logger = useLogstyx({ projectId: 'proj_xyz' });
const batchLogger = new BatchLogger(logger);Middleware Pattern
Add preprocessing to logs:
function createLoggerWithMiddleware(config, middleware) {
const baseLogger = useLogstyx(config);
return {
info: (data) => baseLogger.info(middleware(data)),
warning: (data) => baseLogger.warning(middleware(data)),
error: (data) => baseLogger.error(middleware(data)),
critical: (data) => baseLogger.critical(middleware(data)),
setContext: baseLogger.setContext,
clearContext: baseLogger.clearContext
};
}
// Usage
const logger = createLoggerWithMiddleware(
{ projectId: 'proj_xyz' },
(data) => ({
...data,
timestamp: Date.now(),
hostname: require('os').hostname()
})
);Best Practices
Use Platform-Specific SDKs
For most use cases, use platform-specific SDKs instead of core:
// ❌ Not recommended for Node.js
const { useLogstyx } = require('logstyx-js-core');
// ✅ Better for Node.js
const useLogstyx = require('logstyx-js-node');Structure Your Data
Use structured data instead of plain strings:
// ❌ Avoid
logger.info('User 12345 logged in from 192.168.1.1');
// ✅ Better
logger.info({
message: 'User logged in',
userId: '12345',
ipAddress: '192.168.1.1'
});Leverage Context
Set context once for repeated values:
// ❌ Repetitive
logger.info({ message: 'Action 1', userId: '123', env: 'prod' });
logger.info({ message: 'Action 2', userId: '123', env: 'prod' });
// ✅ Better
logger.setContext({ userId: '123', env: 'prod' });
logger.info({ message: 'Action 1' });
logger.info({ message: 'Action 2' });Handle Sensitive Data
Never log sensitive information:
// ❌ Dangerous
logger.info({ password: user.password, apiKey: config.apiKey });
// ✅ Safe
logger.info({
action: 'user_login',
userId: user.id,
// Omit sensitive fields
});