Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public void doStartup(ModuleContext moduleContext)
UsageMetricsService svc = UsageMetricsService.get();
if (svc != null)
{
svc.registerUsageMetrics(NAME, () -> {
svc.registerUsageMetrics(NAME, metrics -> {
MultiSet<String> secure = new HashMultiSet<>();
MultiSet<String> moderator = new HashMultiSet<>();
CommSchema comm = CommSchema.getInstance();
Expand All @@ -208,14 +208,13 @@ public void doStartup(ModuleContext moduleContext)
boards.getAndIncrement();
});
}
return Map.of(
"messageBoards", boards.get(),
"messages", new TableSelector(comm.getTableInfoAnnouncements()).getRowCount(),
"settings", Map.of(
return metrics
.put("messageBoards", boards.get(), "Containers with at least one message")
.put("messages", () -> new TableSelector(comm.getTableInfoAnnouncements()).getRowCount(), "Messages across all containers, including responses")
.put("settings", Map.of(
"secure", MultiSetUtils.getOccurrenceMap(secure),
"moderatorReview", MultiSetUtils.getOccurrenceMap(moderator)
)
);
), "Message boards counted in messageBoards, by their secure setting value and by their moderator review setting value");
});
}
}
Expand Down
3 changes: 2 additions & 1 deletion api/src/org/labkey/api/assay/actions/DesignerAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.labkey.api.module.ModuleHtmlView;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.security.RequiresPermission;
import org.labkey.api.usageMetrics.AssayMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.view.ActionURL;
Expand Down Expand Up @@ -71,7 +72,7 @@ public ModelAndView getView(DesignerForm form, BindException errors)
String[] files = request.getParameterValues("file");
if (request.getParameter("isRedirect") == null && path != null && files != null && files.length == 1)
{
SimpleMetricsService.get().increment("Assay", "AssayDesigner", "InferDesignFromFileAndImport");
SimpleMetricsService.get().increment(AssayMetricArea.ASSAY_DESIGNER, "InferDesignFromFileAndImport");
ActionURL url = getViewContext().cloneActionURL();
url.addParameter("file", files[0]);
url.addParameter("isRedirect", true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.labkey.api.reports.LabKeyScriptEngineManager;
import org.labkey.api.security.SecurityManager;
import org.labkey.api.settings.AppProps;
import org.labkey.api.usageMetrics.AssayMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.FileUtil;
import org.labkey.api.util.PageFlowUtil;
Expand Down Expand Up @@ -157,9 +158,9 @@ public TransformResult transformAndValidate(
logIfInTransaction(context, operation);

if (operation == TransformOperation.UPDATE)
SimpleMetricsService.get().increment("Assay", "transformScript", "executionCountForUpdate");
SimpleMetricsService.get().increment(AssayMetricArea.TRANSFORM_SCRIPT, "executionCountForUpdate");
if (operation == TransformOperation.INSERT)
SimpleMetricsService.get().increment("Assay", "transformScript", "executionCountForInsert");
SimpleMetricsService.get().increment(AssayMetricArea.TRANSFORM_SCRIPT, "executionCountForInsert");

// TODO: don't bother slurping the contents of binary scripts
StringBuilder sb = new StringBuilder();
Expand Down
5 changes: 2 additions & 3 deletions api/src/org/labkey/api/data/RequestCancellation.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import org.junit.Assert;
import org.junit.Test;
import org.labkey.api.data.dialect.SqlDialect;
import org.labkey.api.module.DefaultModule;
import org.labkey.api.security.User;
import org.labkey.api.usageMetrics.CoreMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.TestContext;
import org.labkey.api.util.logging.LogHelper;
Expand Down Expand Up @@ -57,7 +57,6 @@ public final class RequestCancellation
{
private static final Logger LOG = LogHelper.getLogger(RequestCancellation.class, "Cancellation of in-flight requests and their queries");
// statementCancelled and backendCancelled overlap when a timeout cancels an async query, so they don't add up
private static final String METRIC_AREA = "requestCancel";
private static final long STUCK_AFTER_MS = 10_000;

/** Tests set this to make the driver's cancel signal nothing, forcing the pg_cancel_backend fallback */
Expand Down Expand Up @@ -312,7 +311,7 @@ private static void increment(String metricName)
{
try
{
SimpleMetricsService.get().increment(DefaultModule.CORE_MODULE_NAME, METRIC_AREA, metricName);
SimpleMetricsService.get().increment(CoreMetricArea.REQUEST_CANCEL, metricName);
}
catch (RuntimeException e)
{
Expand Down
3 changes: 3 additions & 0 deletions api/src/org/labkey/api/module/DefaultModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ public void unregister()
final public void startup(ModuleContext moduleContext)
{
doStartup(moduleContext);
// After doStartup() because Core registers SimpleMetricsService there
if (!_controllerNameToClass.isEmpty())
SimpleMetricsService.get().describeFeatureArea(getName(), "controllerHits", "Requests handled by this module's controllers, by controller name");
}

protected abstract void doStartup(ModuleContext moduleContext);
Expand Down
17 changes: 9 additions & 8 deletions api/src/org/labkey/api/query/AbstractQueryImportAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.InsertPermission;
import org.labkey.api.security.permissions.UpdatePermission;
import org.labkey.api.usageMetrics.QueryMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.CPUTimer;
import org.labkey.api.util.FileStream;
Expand Down Expand Up @@ -608,7 +609,7 @@ else if (!dataFileDir.exists())
PipelineService.get().queueJob(job, getQueryImportJobNotificationProviderName());

if (_target != null)
SimpleMetricsService.get().increment("query", "fileBackgroundImports", getMetricPrefix(_target));
SimpleMetricsService.get().increment(QueryMetricArea.FILE_BACKGROUND_IMPORTS, getMetricPrefix(_target));
JSONObject response = new JSONObject();
response.put("success", true);
response.put("jobId", PipelineService.get().getJobId(user, getContainer(), job.getJobGUID()));
Expand Down Expand Up @@ -1006,14 +1007,14 @@ else if (!context.isCrossTypeImport())

public static void addImportValidationErrorMetric(QueryUpdateService.InsertOption insertOption, @Nullable TableInfo target, @Nullable String queryName)
{
String featureArea = insertOption.toString().toLowerCase() + "FailWithValidationError";
QueryMetricArea featureArea = QueryMetricArea.validationErrors(insertOption);
String targetType = null;
if (target != null)
targetType = getMetricPrefix(target);
else if (queryName != null)
targetType = queryName.equalsIgnoreCase("materials") ? "samples" : queryName;
if (targetType != null)
SimpleMetricsService.get().increment("query", featureArea, targetType);
SimpleMetricsService.get().increment(featureArea, targetType);
}

private static String getMetricPrefix(@NotNull TableInfo target)
Expand All @@ -1024,16 +1025,16 @@ private static String getMetricPrefix(@NotNull TableInfo target)

public static void incrementRowCountMetric(int count, QueryUpdateService.InsertOption insertOption, String prefix)
{
String featureArea = "file" + StringUtils.capitalize(insertOption.toString().toLowerCase()) + "Counts";
QueryMetricArea featureArea = QueryMetricArea.rowCounts(insertOption);
String lcPrefix = prefix.toLowerCase();
if (count <= 1000)
SimpleMetricsService.get().increment("query", featureArea, lcPrefix + "1to1000");
SimpleMetricsService.get().increment(featureArea, lcPrefix + "1to1000");
else if (count <= 5000)
SimpleMetricsService.get().increment("query", featureArea, lcPrefix + "1001to5000");
SimpleMetricsService.get().increment(featureArea, lcPrefix + "1001to5000");
else if (count <= 10000)
SimpleMetricsService.get().increment("query", featureArea, lcPrefix + "5001to10000");
SimpleMetricsService.get().increment(featureArea, lcPrefix + "5001to10000");
else
SimpleMetricsService.get().increment("query", featureArea, lcPrefix + "GT10000");
SimpleMetricsService.get().increment(featureArea, lcPrefix + "GT10000");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.labkey.api.ApiModule;
import org.labkey.api.assay.transform.DataTransformService;
import org.labkey.api.attachments.AttachmentParent;
import org.labkey.api.collections.CaseInsensitiveHashSet;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.pipeline.PipeRoot;
import org.labkey.api.pipeline.PipelineService;
import org.labkey.api.query.QueryService.NamedParameterNotProvided;
Expand All @@ -40,6 +38,7 @@
import org.labkey.api.security.SecurityManager;
import org.labkey.api.security.SecurityManager.TransformSession;
import org.labkey.api.thumbnail.Thumbnail;
import org.labkey.api.usageMetrics.ApiMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.DateUtil;
import org.labkey.api.util.ExceptionUtil;
Expand Down Expand Up @@ -418,7 +417,7 @@ protected Object runScript(ScriptEngine engine, ViewContext context, List<ParamR
}
}

SimpleMetricsService.get().increment(ModuleLoader.getInstance().getModule(ApiModule.class).getName(), METRIC_FEATURE_AREA, getClass().getSimpleName() + "-" + engine.getClass().getSimpleName());
SimpleMetricsService.get().increment(ApiMetricArea.REPORT_INVOCATION, getClass().getSimpleName() + "-" + engine.getClass().getSimpleName());

return engine.eval(createScript(engine, context, outputSubst, inputDataTsv, inputParameters));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
package org.labkey.api.reports.report;

import org.apache.logging.log4j.Logger;
import org.labkey.api.ApiModule;
import org.labkey.api.module.ModuleLoader;
import org.labkey.api.query.ValidationException;
import org.labkey.api.reports.ExternalScriptEngine;
import org.labkey.api.reports.report.r.ParamReplacement;
import org.labkey.api.reports.report.r.ParamReplacementSvc;
import org.labkey.api.reports.report.r.view.ConsoleOutput;
import org.labkey.api.usageMetrics.ApiMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.PageFlowUtil;
import org.labkey.api.util.logging.LogHelper;
Expand Down Expand Up @@ -123,7 +122,7 @@ public String runScript(ViewContext context, List<ParamReplacement> outputSubst,

bindings.put(ExternalScriptEngine.WORKING_DIRECTORY, getReportDirFileLike(context.getContainer().getId()).toNioPathForWrite().toString());

SimpleMetricsService.get().increment(ModuleLoader.getInstance().getModule(ApiModule.class).getName(), METRIC_FEATURE_AREA, "Internal-" + engine.getFactory().getEngineName());
SimpleMetricsService.get().increment(ApiMetricArea.REPORT_INVOCATION, "Internal-" + engine.getFactory().getEngineName());

Object output = engine.eval(createScript(engine, context, outputSubst, inputDataTsv, inputParameters));
consolePw.flush();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ public abstract class ScriptEngineReport extends ScriptReport implements Report.
public static final String SUBSTITUTION_MAP = "substitutionMap.txt";
public static final String CONSOLE_OUTPUT = "console.txt";

public static final String METRIC_FEATURE_AREA = "ReportInvocation";

private static final Logger LOG = LogManager.getLogger(ScriptEngineReport.class);

Expand Down
4 changes: 2 additions & 2 deletions api/src/org/labkey/api/reports/report/python/IpynbReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import org.labkey.api.ApiModule;
import org.labkey.api.data.Container;
import org.labkey.api.data.DbScope;
import org.labkey.api.module.ModuleLoader;
Expand All @@ -47,6 +46,7 @@
import org.labkey.api.reports.report.r.view.ConsoleOutput;
import org.labkey.api.reports.report.r.view.IpynbOutput;
import org.labkey.api.security.SessionApiKeyManager;
import org.labkey.api.usageMetrics.ApiMetricArea;
import org.labkey.api.usageMetrics.SimpleMetricsService;
import org.labkey.api.util.CSRFUtil;
import org.labkey.api.util.ConfigurationException;
Expand Down Expand Up @@ -380,7 +380,7 @@ public int execute(ViewContext context, String apiKey, FileLike working, FileLik

tryPing(service);

SimpleMetricsService.get().increment(ModuleLoader.getInstance().getModule(ApiModule.class).getName(), ScriptEngineReport.METRIC_FEATURE_AREA, getClass().getSimpleName());
SimpleMetricsService.get().increment(ApiMetricArea.REPORT_INVOCATION, getClass().getSimpleName());

try (CloseableHttpClient client = HttpClients.createDefault())
{
Expand Down
4 changes: 2 additions & 2 deletions api/src/org/labkey/api/security/AuthenticationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1890,11 +1890,11 @@ public static URLHelper getAfterLoginURL(Container current, @Nullable LoginRetur

public static void registerMetricsProvider()
{
UsageMetricsService.get().registerUsageMetrics(ModuleLoader.getInstance().getCoreModule().getName(), () -> {
UsageMetricsService.get().registerUsageMetrics(ModuleLoader.getInstance().getCoreModule().getName(), metrics -> {
Map<String, Long> map = AuthenticationConfigurationCache.getActive(AuthenticationConfiguration.class).stream()
.collect(Collectors.groupingBy(config->config.getAuthenticationProvider().getName(), Collectors.counting()));

return Collections.singletonMap("authenticationConfigurations", map);
return metrics.put("authenticationConfigurations", map, "Enabled authentication configurations by provider (Database, LDAP, SAML, CAS, etc.)");
});
}

Expand Down
4 changes: 2 additions & 2 deletions api/src/org/labkey/api/security/SecurityManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private static class Authentication {}

public static UsageMetricsProvider getMetricsProvider()
{
return () -> {
return metrics -> {
MultiValuedMap<Role, UserPrincipal> mvm = new HashSetValuedHashMap<>();
Set<Role> siteRoles = RoleManager.getSiteRoles();
for (RoleAssignment assignment : ContainerManager.getRoot().getPolicy().getAssignments())
Expand All @@ -233,7 +233,7 @@ public static UsageMetricsProvider getMetricsProvider()
Map<String, Integer> roleCounts = mvm.keySet().stream()
.collect(Collectors.toMap(Role::getName, key -> mvm.get(key).size()));

return Map.of("SiteRoleUserCounts", roleCounts);
return metrics.put("SiteRoleUserCounts", roleCounts, "Active users holding each site-level role, directly or through group membership");
};
}

Expand Down
35 changes: 35 additions & 0 deletions api/src/org/labkey/api/usageMetrics/ApiMetricArea.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2026 LabKey Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.labkey.api.usageMetrics;

public enum ApiMetricArea implements SimpleMetricArea
{
FILE_SERVLET("FileServlet", "/files/ URLs forwarded to the FileContent module's sendFile action (urlsDispatched)"),
REPORT_INVOCATION("ReportInvocation", "Script report executions by report and engine: <report class>-<engine class> for external engines, Internal-<engine name> for in-process engines, and the report class for Jupyter notebook reports");

private final Definition _definition;

ApiMetricArea(String areaName, String description)
{
_definition = new Definition("API", areaName, description);
}

@Override
public Definition getDefinition()
{
return _definition;
}
}
37 changes: 37 additions & 0 deletions api/src/org/labkey/api/usageMetrics/AssayMetricArea.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2026 LabKey Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.labkey.api.usageMetrics;

public enum AssayMetricArea implements SimpleMetricArea
{
ASSAY_DESIGNER("AssayDesigner", "Assay imports started from files selected in the file browser: InferDesignFromFileAndImport creates a design inferred from one file, ImportFromFile imports into an existing design"),
TRANSFORM_SCRIPT("transformScript", "Assay transform script executions, by operation (executionCountForInsert, executionCountForUpdate)"),
PLATE("plate", "Plate export requests, by format (exportCSV, exportTSV, exportMAP)"),
PLATE_SET("plateSet", "Plate set worklist exports (exportWorklist)");

private final Definition _definition;

AssayMetricArea(String areaName, String description)
{
_definition = new Definition("Assay", areaName, description);
}

@Override
public Definition getDefinition()
{
return _definition;
}
}
37 changes: 37 additions & 0 deletions api/src/org/labkey/api/usageMetrics/CoreMetricArea.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2026 LabKey Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.labkey.api.usageMetrics;

import org.labkey.api.module.DefaultModule;

public enum CoreMetricArea implements SimpleMetricArea
{
CLIENT_API_REQUESTS("clientApiRequests", "Requests from LabKey client API libraries, by library (Java, Python, R, etc.), identified by user agent"),
REQUEST_CANCEL("requestCancel", "Cancelled requests by source (timeout, clientDisconnect; shutdown counts database backends instead), plus statementCancelled: running statements cancelled, and backendCancelled: database backends signalled");

private final Definition _definition;

CoreMetricArea(String areaName, String description)
{
_definition = new Definition(DefaultModule.CORE_MODULE_NAME, areaName, description);
}

@Override
public Definition getDefinition()
{
return _definition;
}
}
Loading
Loading