Skip to content

Document logs essentials restricted ESQL commands for Kibana #130346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public Set<String> getSupportedOptions() {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(
"org.elasticsearch.xpack.esql.SupportsObservabilityTier",
"org.elasticsearch.core.Nullable",
"org.elasticsearch.injection.guice.Inject",
"org.elasticsearch.xpack.esql.expression.function.FunctionInfo",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.esql;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SupportsObservabilityTier {

ObservabilityTier tier();

enum ObservabilityTier {
COMPLETE,
LOGS_ESSENTIALS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.esql.LicenseAware;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Nullability;
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
Expand All @@ -27,6 +28,7 @@
import java.io.IOException;
import java.util.List;

import static org.elasticsearch.xpack.esql.SupportsObservabilityTier.ObservabilityTier.COMPLETE;
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;

Expand All @@ -39,6 +41,7 @@
* For the implementation, see {@link org.elasticsearch.compute.aggregation.blockhash.CategorizeBlockHash}
* </p>
*/
@SupportsObservabilityTier(tier = COMPLETE)
public class Categorize extends GroupingFunction.NonEvaluatableGroupingFunction implements LicenseAware {
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
Expression.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.elasticsearch.compute.operator.ChangePointOperator;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.esql.LicenseAware;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier;
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisVerificationAware;
import org.elasticsearch.xpack.esql.common.Failures;
import org.elasticsearch.xpack.esql.core.expression.Attribute;
Expand All @@ -27,6 +28,7 @@
import java.util.List;
import java.util.Objects;

import static org.elasticsearch.xpack.esql.SupportsObservabilityTier.ObservabilityTier.COMPLETE;
import static org.elasticsearch.xpack.esql.common.Failure.fail;

/**
Expand All @@ -41,6 +43,7 @@
* Furthermore, ChangePoint should be called with at most 1000 data points. That's
* enforced by the Limit in the surrogate plan.
*/
@SupportsObservabilityTier(tier = COMPLETE)
public class ChangePoint extends UnaryPlan implements SurrogateLogicalPlan, PostAnalysisVerificationAware, LicenseAware {

private final Attribute value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.esql.CsvTestsDataLoader;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier.ObservabilityTier;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.fulltext.MatchOperator;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.regex.RLike;
Expand Down Expand Up @@ -572,7 +574,7 @@ private void renderDocs(FunctionDefinition definition) throws Exception {
boolean hasAppendix = renderAppendix(info.appendix());
renderFullLayout(info, hasExamples, hasAppendix, hasFunctionOptions);
renderKibanaInlineDocs(name, null, info);
renderKibanaFunctionDefinition(name, null, info, description.args(), description.variadic());
renderKibanaFunctionDefinition(name, null, info, description.args(), description.variadic(), getObservabilityTier());
}

private void renderFunctionNamedParams(EsqlFunctionRegistry.MapArgSignature mapArgSignature) throws IOException {
Expand Down Expand Up @@ -666,6 +668,11 @@ private String addInclude(String section) {
:::
""".replace("$NAME$", name).replace("$SECTION$", section);
}

private ObservabilityTier getObservabilityTier() {
SupportsObservabilityTier supportsObservabilityTier = definition.clazz().getAnnotation(SupportsObservabilityTier.class);
return supportsObservabilityTier != null ? supportsObservabilityTier.tier() : null;
}
}

/** Operator specific docs generating, since it is currently quite different from the function docs generating */
Expand Down Expand Up @@ -712,7 +719,7 @@ public void renderDocs() throws Exception {
if (ctor != null) {
FunctionInfo functionInfo = ctor.getAnnotation(FunctionInfo.class);
assert functionInfo != null;
renderDocsForOperators(op.name(), op.titleName(), ctor, functionInfo, op.variadic());
renderDocsForOperators(op.name(), op.titleName(), ctor, functionInfo, op.variadic(), getObservabilityTier());
} else {
logger.info("Skipping rendering docs for operator '" + op.name() + "' with no @FunctionInfo");
}
Expand Down Expand Up @@ -787,11 +794,17 @@ public Example[] examples() {
}
};
String name = "not_" + baseName;
renderDocsForOperators(name, null, ctor, functionInfo, op.variadic());
renderDocsForOperators(name, null, ctor, functionInfo, op.variadic(), getObservabilityTier());
}

void renderDocsForOperators(String name, String titleName, Constructor<?> ctor, FunctionInfo info, boolean variadic)
throws Exception {
void renderDocsForOperators(
String name,
String titleName,
Constructor<?> ctor,
FunctionInfo info,
boolean variadic,
ObservabilityTier observabilityTier
) throws Exception {
renderKibanaInlineDocs(name, titleName, info);

var params = ctor.getParameters();
Expand All @@ -808,7 +821,7 @@ void renderDocsForOperators(String name, String titleName, Constructor<?> ctor,
}
}
}
renderKibanaFunctionDefinition(name, titleName, info, args, variadic);
renderKibanaFunctionDefinition(name, titleName, info, args, variadic, observabilityTier);
renderDetailedDescription(info.detailedDescription(), info.note());
renderTypes(name, args);
renderExamples(info);
Expand All @@ -831,17 +844,35 @@ void renderDetailedDescription(String detailedDescription, String note) throws I
writeToTempSnippetsDir("detailedDescription", rendered.toString());
}
}

private ObservabilityTier getObservabilityTier() {
if (op != null) {
SupportsObservabilityTier supportsObservabilityTier = op.clazz().getAnnotation(SupportsObservabilityTier.class);
if (supportsObservabilityTier != null) {
return supportsObservabilityTier.tier();
}
}
return null;
}
}

/** Command specific docs generating, currently very empty since we only render kibana definition files */
public static class CommandsDocsSupport extends DocsV3Support {
private final LogicalPlan command;
private final XPackLicenseState licenseState;
private final ObservabilityTier observabilityTier;

public CommandsDocsSupport(String name, Class<?> testClass, LogicalPlan command, XPackLicenseState licenseState) {
public CommandsDocsSupport(
String name,
Class<?> testClass,
LogicalPlan command,
XPackLicenseState licenseState,
ObservabilityTier observabilityTier
) {
super("commands", name, testClass, Map::of);
this.command = command;
this.licenseState = licenseState;
this.observabilityTier = observabilityTier;
}

@Override
Expand All @@ -867,6 +898,9 @@ void renderKibanaCommandDefinition() throws Exception {
if (license != null && license != License.OperationMode.BASIC) {
builder.field("license", license.toString());
}
if (observabilityTier != null && observabilityTier != ObservabilityTier.LOGS_ESSENTIALS) {
builder.field("observability_tier", observabilityTier.toString());
}
String rendered = Strings.toString(builder.endObject());
logger.info("Writing kibana command definition for [{}]:\n{}", name, rendered);
writeToTempKibanaDir("definition", "json", rendered);
Expand Down Expand Up @@ -1044,7 +1078,8 @@ void renderKibanaFunctionDefinition(
String titleName,
FunctionInfo info,
List<EsqlFunctionRegistry.ArgSignature> args,
boolean variadic
boolean variadic,
ObservabilityTier observabilityTier
) throws Exception {

try (XContentBuilder builder = JsonXContent.contentBuilder().prettyPrint().lfAtEnd().startObject()) {
Expand All @@ -1067,6 +1102,9 @@ void renderKibanaFunctionDefinition(
if (license != null && license != License.OperationMode.BASIC) {
builder.field("license", license.toString());
}
if (observabilityTier != null && observabilityTier != ObservabilityTier.LOGS_ESSENTIALS) {
builder.field("observability_tier", observabilityTier.toString());
}
if (titleName != null && titleName.equals(name) == false) {
builder.field("titleName", titleName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import org.elasticsearch.logging.Logger;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.esql.LicenseAware;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier;
import org.elasticsearch.xpack.esql.SupportsObservabilityTier.ObservabilityTier;
import org.elasticsearch.xpack.esql.core.tree.Node;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.expression.function.DocsV3Support;
Expand Down Expand Up @@ -74,21 +76,32 @@ private static XPackLicenseState makeLicenseState(License.OperationMode mode) {
private static void checkLicense(String commandName, LogicalPlan command) throws Exception {
log.info("Running function license checks");
TestCheckLicense checkLicense = new TestCheckLicense();
ObservabilityTier observabilityTier = null;
SupportsObservabilityTier supportsObservabilityTier = command.getClass().getAnnotation(SupportsObservabilityTier.class);
if (supportsObservabilityTier != null) {
observabilityTier = supportsObservabilityTier.tier();
}
if (command instanceof LicenseAware licenseAware) {
log.info("Command " + commandName + " implements LicenseAware.");
saveLicenseState(commandName, command, checkLicense.licenseLevel(licenseAware));
saveLicenseState(commandName, command, checkLicense.licenseLevel(licenseAware), observabilityTier);
} else {
log.info("Command " + commandName + " does not implement LicenseAware.");
saveLicenseState(commandName, command, checkLicense.basicLicense);
saveLicenseState(commandName, command, checkLicense.basicLicense, observabilityTier);
}
}

private static void saveLicenseState(String name, LogicalPlan command, XPackLicenseState licenseState) throws Exception {
private static void saveLicenseState(
String name,
LogicalPlan command,
XPackLicenseState licenseState,
SupportsObservabilityTier.ObservabilityTier observabilityTier
) throws Exception {
DocsV3Support.CommandsDocsSupport docs = new DocsV3Support.CommandsDocsSupport(
name.toLowerCase(Locale.ROOT),
CommandLicenseTests.class,
command,
licenseState
licenseState,
observabilityTier
);
docs.renderDocs();
}
Expand Down
Loading