This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new 47108329dbe branch-4.0: [feature](QueryPlanAction)add sql received
from table query plan action into audit log #58739 (#59041)
47108329dbe is described below
commit 47108329dbe97f63b19ac2d57fa4d19971351a12
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Dec 18 12:26:16 2025 +0800
branch-4.0: [feature](QueryPlanAction)add sql received from table query
plan action into audit log #58739 (#59041)
Cherry-picked from #58739
Co-authored-by: starocean999 <[email protected]>
---
.../doris/httpv2/rest/TableQueryPlanAction.java | 119 ++++++++++++++++++++-
.../java/org/apache/doris/qe/AuditLogHelper.java | 2 +-
2 files changed, 118 insertions(+), 3 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java
index 2bb5d48ac06..c7b2b9e81b4 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/TableQueryPlanAction.java
@@ -22,10 +22,15 @@ import org.apache.doris.catalog.Database;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.TableIf;
+import org.apache.doris.cloud.qe.ComputeGroupException;
+import org.apache.doris.cluster.ClusterNamespace;
import org.apache.doris.common.Config;
import org.apache.doris.common.DorisHttpException;
import org.apache.doris.common.MetaNotFoundException;
+import org.apache.doris.common.util.DebugUtil;
import org.apache.doris.common.util.NetUtils;
+import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.datasource.InternalCatalog;
import org.apache.doris.httpv2.entity.ResponseEntityBuilder;
import org.apache.doris.httpv2.rest.manager.HttpUtils;
import org.apache.doris.mysql.privilege.PrivPredicate;
@@ -36,6 +41,7 @@ import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.trees.plans.commands.Command;
import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
+import org.apache.doris.nereids.trees.plans.commands.NeedAuditEncryption;
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
@@ -46,9 +52,15 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalSubQueryAlias;
import org.apache.doris.nereids.util.RelationUtil;
import org.apache.doris.planner.PlanFragment;
import org.apache.doris.planner.ScanNode;
+import org.apache.doris.plugin.AuditEvent;
+import org.apache.doris.qe.AuditLogHelper;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.GlobalVariable;
+import org.apache.doris.qe.QueryState;
import org.apache.doris.qe.SessionVariable;
+import org.apache.doris.qe.SqlModeHelper;
+import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.service.FrontendOptions;
import org.apache.doris.thrift.TDataSink;
import org.apache.doris.thrift.TDataSinkType;
import org.apache.doris.thrift.TMemoryScratchSink;
@@ -64,6 +76,7 @@ import com.google.common.base.Strings;
import io.netty.handler.codec.http.HttpResponseStatus;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;
@@ -235,6 +248,12 @@ public class TableQueryPlanAction extends
RestBaseController {
+ "." + tableName + "]");
}
}
+ UUID uuid = UUID.randomUUID();
+ TUniqueId queryId = new TUniqueId(uuid.getMostSignificantBits(),
uuid.getLeastSignificantBits());
+ context.setQueryId(queryId);
+ context.setStartTime();
+ context.setSqlHash(DigestUtils.md5Hex(sql));
+
NereidsPlanner nereidsPlanner = new
NereidsPlanner(context.getStatementContext());
LogicalPlan rewrittenPlan = (LogicalPlan)
nereidsPlanner.planWithLock(parsedPlan,
PhysicalProperties.GATHER,
ExplainCommand.ExplainLevel.REWRITTEN_PLAN);
@@ -282,8 +301,8 @@ public class TableQueryPlanAction extends
RestBaseController {
tQueryPlanInfo.plan_fragment = tPlanFragment;
tQueryPlanInfo.desc_tbl = planner.getDescTable().toThrift();
// set query_id
- UUID uuid = UUID.randomUUID();
- tQueryPlanInfo.query_id = new
TUniqueId(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
+ tQueryPlanInfo.query_id = queryId;
+
Map<Long, TTabletVersionInfo> tabletInfo = new HashMap<>();
// acquire resolved tablet distribution
@@ -309,6 +328,7 @@ public class TableQueryPlanAction extends
RestBaseController {
result.put("partitions", tabletRoutings);
result.put("opaqued_query_plan", opaquedQueryPlan);
result.put("status", 200);
+ addToAuditLog(context, sql, query);
} finally {
if (needSetParallelResultSinkToFalse) {
sessionVariable.setParallelResultSink(false);
@@ -317,6 +337,101 @@ public class TableQueryPlanAction extends
RestBaseController {
}
+ private static void addToAuditLog(ConnectContext ctx, String origStmt,
StatementBase parsedStmt) {
+ // slow query
+ long elapseMs = System.currentTimeMillis() - ctx.getStartTime();
+ CatalogIf catalog = ctx.getCurrentCatalog();
+ String cluster = "";
+ try {
+ if (Config.isCloudMode()) {
+ cluster = ctx.getCloudCluster(false);
+ }
+ } catch (ComputeGroupException e) {
+ LOG.warn("Failed to get cloud cluster", e);
+ }
+
+ AuditEvent.AuditEventBuilder auditEventBuilder =
ctx.getAuditEventBuilder();
+ // ATTN: MUST reset, otherwise, the same AuditEventBuilder instance
will be used in the next query.
+ auditEventBuilder.reset();
+ auditEventBuilder
+ .setEventType(AuditEvent.EventType.AFTER_QUERY)
+ .setQueryId(DebugUtil.printId(ctx.queryId()))
+ .setTimestamp(ctx.getStartTime())
+ .setClientIp(ctx.getClientIP())
+
.setUser(ClusterNamespace.getNameFromFullName(ctx.getQualifiedUser()))
+ .setFeIp(FrontendOptions.getLocalHostAddress())
+ .setCtl(catalog == null ?
InternalCatalog.INTERNAL_CATALOG_NAME : catalog.getName())
+ .setDb(ClusterNamespace.getNameFromFullName(ctx.getDatabase()))
+ .setState(ctx.getState().toString())
+ .setErrorCode(ctx.getState().getErrorCode() == null ? 0 :
ctx.getState().getErrorCode().getCode())
+ .setErrorMessage((ctx.getState().getErrorMessage() == null ?
"" :
+ ctx.getState().getErrorMessage().replace("\n", "
").replace("\t", " ")))
+ .setQueryTime(elapseMs)
+ .setCpuTimeMs(0)
+ .setPeakMemoryBytes(0)
+ .setScanBytes(0)
+ .setScanRows(0)
+ .setReturnRows(ctx.getReturnRows())
+ .setSpillWriteBytesToLocalStorage(0)
+ .setSpillReadBytesFromLocalStorage(0)
+ .setScanBytesFromLocalStorage(0)
+ .setScanBytesFromRemoteStorage(0)
+ .setFuzzyVariables("")
+ .setCommandType("HttpPlan")
+ .setStmtType("SELECT")
+ .setStmtId(ctx.getStmtId())
+ .setSqlHash(ctx.getSqlHash())
+ .setIsQuery(true)
+ .setIsNereids(true)
+ .setisInternal(false)
+ .setCloudCluster(Strings.isNullOrEmpty(cluster) ? "UNKNOWN" :
cluster)
+ .setWorkloadGroup(ctx.getWorkloadGroupName());
+
+ // sql mode
+ SessionVariable sessionVariable = ctx.getSessionVariable();
+ if (sessionVariable != null) {
+ try {
+
auditEventBuilder.setSqlMode(SqlModeHelper.decode(sessionVariable.getSqlMode()));
+ } catch (Exception e) {
+ LOG.warn("decode sql mode {} failed.",
sessionVariable.getSqlMode(), e);
+ }
+ }
+
+ boolean isSyntaxErr = ctx.getState().getStateType() ==
QueryState.MysqlStateType.ERR
+ && ctx.getState().getErrType() ==
QueryState.ErrType.SYNTAX_PARSE_ERR;
+ String encryptSql = isSyntaxErr ? "Syntax Error" : origStmt;
+ if (isSyntaxErr) {
+ auditEventBuilder.setErrorMessage("Syntax Error");
+ }
+ // We put origin query stmt at the end of audit log, for parsing the
log more convenient.
+ LogicalPlan logicalPlan = ((LogicalPlanAdapter)
parsedStmt).getLogicalPlan();
+ if ((logicalPlan instanceof NeedAuditEncryption)) {
+ encryptSql = ((NeedAuditEncryption)
logicalPlan).geneEncryptionSQL(origStmt);
+ }
+
+ int maxLen = GlobalVariable.auditPluginMaxSqlLength;
+ encryptSql = AuditLogHelper.truncateByBytes(encryptSql, maxLen,
+ " ... /* truncated. audit_plugin_max_sql_length=" + maxLen + "
*/");
+ auditEventBuilder.setStmt(encryptSql);
+
+ if (!Env.getCurrentEnv().isMaster()) {
+ StmtExecutor executor = ctx.getExecutor();
+ if (executor != null && executor.isForwardToMaster()) {
+ auditEventBuilder.setState(executor.getProxyStatus());
+ int proxyStatusCode = executor.getProxyStatusCode();
+ if (proxyStatusCode != 0) {
+ auditEventBuilder.setErrorCode(proxyStatusCode);
+
auditEventBuilder.setErrorMessage(executor.getProxyErrMsg());
+ }
+ }
+ }
+ AuditEvent event = auditEventBuilder.build();
+
Env.getCurrentEnv().getWorkloadRuntimeStatusMgr().submitFinishQueryToAudit(event);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("submit audit event: {}", event.queryId);
+ }
+ }
+
/**
* acquire all involved (already pruned) tablet routing
*
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
index ad6c3bee6d0..fb2017f1395 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/AuditLogHelper.java
@@ -139,7 +139,7 @@ public class AuditLogHelper {
}
}
- private static String truncateByBytes(String str, int maxLen, String
suffix) {
+ public static String truncateByBytes(String str, int maxLen, String
suffix) {
// use `getBytes().length` to get real byte length
if (maxLen >= str.getBytes().length) {
return str;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]