Logging
The Descope backend SDKs can emit log messages to help you diagnose failed API calls, session validation errors, and configuration problems.
Support differs by language, and the SDKs do not share a common shape. Some take a log level, some take a logger object you supply.
| SDK | What you set | Log levels |
|---|---|---|
| Node.js | A logger object | None, so you filter inside the logger you supply |
| Go | LogLevel, and optionally a Logger of your own | logger.LogNone, logger.LogInfoLevel, and logger.LogDebugLevel |
| Ruby | log_level, and optionally a logger of your own | debug, info, warn, error, fatal, and unknown |
| PHP | debug to true or false | None |
Enabling a Logger
The Node.js SDK takes a logger object rather than a log level. Pass it alongside projectId:
import DescopeClient from '@descope/node-sdk';
const descopeClient = DescopeClient({
projectId: '__ProjectID__',
logger: console,
});During development you can pass console directly. To route messages into your own logging service, supply an object with debug, log, warn, and error methods:
const logger = {
debug: (message, ...args) => myLogger.debug(message, ...args),
log: (message, ...args) => myLogger.info(message, ...args),
warn: (message, ...args) => myLogger.warn(message, ...args),
error: (message, ...args) => myLogger.error(message, ...args),
};
const descopeClient = DescopeClient({
projectId: '__ProjectID__',
logger,
});All four methods are required, which is why passing console works. The SDK calls error for failures, log for informational messages, and warn for non-fatal problems. Messages are emitted for session validation failures, refresh token validation failures, access key exchange failures, public key parsing errors, and license handshake problems.
The Go SDK takes a log level, and optionally a logger of your own. Set them on client.Config:
import (
"github.com/descope/go-sdk/descope/client"
"github.com/descope/go-sdk/descope/logger"
)
descopeClient, err := client.NewWithConfig(&client.Config{
ProjectID: "__ProjectID__",
LogLevel: logger.LogDebugLevel,
})Supported Log Levels
logger.LogNonelogs nothing. This is the default.logger.LogInfoLevellogs errors and informational messages.logger.LogDebugLevellogs errors, informational messages, and debug detail.
Supplying Your Own Logger
If you leave Logger unset, the SDK writes to Go's log.Default(). To send messages elsewhere, provide any type with a Print(v ...any) method:
type myLogger struct{}
func (myLogger) Print(v ...any) {
// forward to your logging library
}
descopeClient, err := client.NewWithConfig(&client.Config{
ProjectID: "__ProjectID__",
LogLevel: logger.LogDebugLevel,
Logger: myLogger{},
})The Ruby SDK takes a log level. Unlike every other Descope SDK, it logs by default at info:
require 'descope'
client = Descope::Client.new(
project_id: '__ProjectID__',
log_level: 'debug'
)Log messages go to STDOUT, prefixed with the timestamp, severity, and your Project ID:
[2026-01-14 09:22:41 UTC] INFO PRID: __ProjectID__ Descope::Client: Initializing Descope APISupported Log Levels
log_level accepts the names of Ruby's standard Logger levels: debug, info, warn, error, fatal, and unknown. An unrecognized value raises a NameError, so keep to that list.
The level is resolved in this order:
- The
log_leveloption passed toDescope::Client.new - The
DESCOPE_LOG_LEVELenvironment variable info, if neither is set
To minimize log output, override the default by setting the level explicitly:
client = Descope::Client.new(
project_id: '__ProjectID__',
log_level: 'error'
)Supplying Your Own Logger
Passing a logger takes over completely. When you supply one, log_level is ignored and the level is whatever your logger is configured with:
require 'logger'
client = Descope::Client.new(
project_id: '__ProjectID__',
logger: Logger.new($stdout, level: Logger::WARN)
)The PHP SDK takes a boolean. Set debug in the config array you pass to DescopeSDK:
require 'vendor/autoload.php';
use Descope\SDK\DescopeSDK;
$descopeSDK = new DescopeSDK([
'projectId' => $_ENV['DESCOPE_PROJECT_ID'],
'debug' => true,
]);There are no levels. When debug is on, a failed POST, GET, or DELETE request to the Descope API is written to PHP's error log through error_log():
[04-Aug-2026 18:47:06 UTC] Descope SDK [POST] Error: HTTP Status Code: 400, Response: {"errorCode":"E061102","errorDescription":"One time code is invalid"}Messages are prefixed with Descope SDK and tagged with the HTTP method, so you can filter for them. Because they go through error_log(), they land wherever your PHP installation sends error output: the path set by error_log in your php.ini, your web server's error log.
Enabling It Without Changing Code
You can also turn debug logging on with the DESCOPE_DEBUG environment variable, most reliably from a .env file:
DESCOPE_DEBUG=trueThe value must be the exact lowercase string true. 1, TRUE, and on are ignored. The SDK reads this from PHP's $_ENV superglobal, so a variable exported only into the process environment is not picked up unless your php.ini variables_order includes E, which it does not by default. Setting debug in the config array always takes precedence over the environment variable.