Skip to main content

Usage Logs

warning

This feature is experimental and may change in future releases.

info

Usage logs are available in v.26.1.5 or later.

Asprova My Schedule / WS server can record usage logs of events such as logins, logouts, and data access.

Enabling Usage Logs

warning

Enabling usage logs may impact the performance of the server detrimentally.

Usage logging is disabled by default. To enable it, set the following in your server's config.json:

{
"enable_usage_log": true
}

Configuration

SettingDefaultDescription
enable_usage_logfalseEnable or disable usage logging.
usage_log_retention"30d"How long to keep usage log files before they are automatically deleted.

Log Files

Usage log files are written to the logs directory inside your server's data directory (the user_file_directory). By default, this is:

C:\ProgramData\Asprova\Asprova My Schedule\WS\logs

Each day produces a separate file named usage-YYYY-MM-DD.log. Files are rotated automatically and old files are removed based on the retention setting.

Each line in a log file is a JSON object containing:

  • eventType — the type of event (see below).
  • user — the user who triggered the event, if applicable.
    • userId — the user's ID.
    • username — the user's name.
  • req — information about the HTTP request that triggered the event, if applicable.
    • method — the HTTP method (e.g. POST).
    • path — the request path (e.g. /api/auth/login).
    • ip — the client's IP address.
  • details — additional context specific to the event.
  • timestamp — when the event occurred.

Events

The following events are recorded:

EventDescription
ws:server:startThe server has started.
ws:user:createdA user has been created.
ws:user:login:successA user logged in successfully.
ws:user:login:failureA login attempt failed.
ws:user:logoutA user logged out.
ws:user:removedA user has been removed.
my-schedule:license:ccu:addedA new concurrent user connection was accepted.
my-schedule:license:ccu:max-reachedA connection was rejected because the concurrent user limit was reached.
my-schedule:license:ccu:released-with-logoutA concurrent connection was released with user logging out.
my-schedule:license:ccu:expiredA concurrent connection reserved by a user was released with expiration.
my-schedule:license:maintenance-mode:enteredThe server entered the maintenance mode.
my-schedule:license:maintenance-mode:leftThe server left the maintenance mode.
my-schedule:view:accessedA user accessed a view.
my-schedule:view:uploadedData was uploaded to a view.

ws:server:start

The server has started.

Detail fieldDescription
portThe port the server is listening on.
httpsWhether HTTPS is enabled.

ws:user:created

A user has been created.

Detail fieldDescription
idThe new user's ID.
usernameThe new user's name.
typeThe new user's login method.
rolesThe new user's roles.

ws:user:login:success

A user logged in successfully. No additional detail fields.

ws:user:login:failure

A user attempted to log in but failed. Note that user field is not available for this event.

Detail fieldDescription
usernameThe username used in the attempt.
reasonThe reason why the attempt failed.

ws:user:removed

A user has been removed. Note that user field points to the user who performed the removal.

Detail fieldDescription
idThe removed user's ID.
usernameThe removed user's name.

ws:user:logout

A user logged out. No additional detail fields.

my-schedule:license:ccu:added

A new concurrent user connection was accepted.

Detail fieldDescription
poolThe CCU pool the user has been added.
currentCountThe current number of concurrent users after the connection was added.
maxCountThe maximum number of concurrent users allowed for this plan.

my-schedule:license:ccu:max-reached

A connection was rejected because the concurrent user limit was reached.

Detail fieldDescription
poolThe CCU pool the max has been reached.
currentCountThe current number of concurrent users for this plan.
maxCountThe maximum number of concurrent users allowed for this plan.

my-schedule:license:ccu:released-with-logout

A concurrent connection count was released with logout. One event is emitted per pool.

Detail fieldDescription
poolThe CCU pool the user has been removed from.
currentCountThe current number of concurrent users for this plan.
maxCountThe maximum number of concurrent users allowed for this plan.

my-schedule:license:ccu:expired

A concurrent connection was expired due to inactivity, and therefore, released. One event is emitted per pool.

Detail fieldDescription
poolThe CCU pool the user has been removed from.
currentCountThe current number of concurrent users for this plan.
maxCountThe maximum number of concurrent users allowed for this plan.

my-schedule:license:maintenance-mode:entered

The server entered the maintenance mode due to insufficient license available. No additional detail fields.

my-schedule:license:maintenance-mode:left

The server left the maintenance mode. No additional detail fields.

my-schedule:view:accessed

A user accessed a view. This event is deduplicated — only one entry is logged per user, project, and view within a short time window.

Detail fieldDescription
projectIdThe project ID.
viewNameThe name of the view (e.g. ResGantt, DispatchingView).

my-schedule:view:uploaded

Data was uploaded to a view. This event is deduplicated — only one entry is logged per user, project, and view within a short time window.

Detail fieldDescription
projectIdThe project ID.
viewNameThe name of the view (e.g. ResGantt, DispatchingView).

Analyzing Logs with DuckDB

Usage log files are newline-delimited JSON (NDJSON), which DuckDB can query directly without any import step.

Setup

Open a terminal in the logs directory and launch DuckDB:

cd "C:\ProgramData\Asprova\Asprova My Schedule\WS\logs"
duckdb

Then create a view to avoid repeating the file path in every query. Use a glob pattern to include all log files:

CREATE VIEW usage_logs AS SELECT * FROM read_json_auto('usage-*.log', filename=true);

To query a single day instead:

CREATE VIEW usage_logs AS SELECT * FROM read_json_auto('usage-2026-04-10.log');

Counting events by type

SELECT eventType, count(*) AS count
FROM usage_logs
GROUP BY eventType
ORDER BY count DESC;

Listing login failures

SELECT timestamp, details.username, req.ip
FROM usage_logs
WHERE eventType = 'ws:user:login:failure'
ORDER BY timestamp;

Active users by view access

SELECT "user".username, details.projectId, details.viewName, count(*) AS accesses
FROM usage_logs
WHERE eventType = 'my-schedule:view:accessed'
GROUP BY "user".username, details.projectId, details.viewName
ORDER BY accesses DESC;

CCU usage over time

SELECT timestamp, eventType, details.pool, details.currentCount, details.maxCount
FROM usage_logs
WHERE eventType LIKE 'my-schedule:license:ccu:%'
ORDER BY timestamp;