Skip to content

Commit 105675f

Browse files
committed
#AI commit# 开发阶段: 日志打印优化,spark init sql 异常优化
1 parent 994647b commit 105675f

26 files changed

Lines changed: 399 additions & 444 deletions

File tree

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.linkis.common.utils;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
/**
23+
* Code masking utility for security logging. Prevents sensitive user code from being logged in
24+
* plain text.
25+
*/
26+
public class CodeUtils {
27+
28+
/** Default maximum preview length */
29+
private static final int DEFAULT_MAX_PREVIEW_LENGTH = 50;
30+
31+
/** Default preview length for code snippet (first N characters) */
32+
private static final int DEFAULT_CODE_SNIPPET_LENGTH = 6;
33+
34+
/** Maximum lines to preview */
35+
private static final int MAX_PREVIEW_LINES = 3;
36+
37+
/**
38+
* Mask code for logging - only shows length and line count
39+
*
40+
* @param code the code to mask
41+
* @return masked code information
42+
*/
43+
public static String maskCode(String code) {
44+
if (StringUtils.isBlank(code)) {
45+
return "[empty code]";
46+
}
47+
int length = code.length();
48+
int lines = code.split("\n", -1).length;
49+
return String.format("[code length: %d, lines: %d]", length, lines);
50+
}
51+
52+
/**
53+
* Mask code for logging - shows type, length, line count, and code snippet
54+
*
55+
* @param code the code to mask
56+
* @param codeType the type of code (e.g., "SQL", "Scala", "Python")
57+
* @return masked code information with snippet
58+
*/
59+
public static String maskCode(String code, String codeType) {
60+
if (StringUtils.isBlank(code)) {
61+
return "[empty " + codeType + " code]";
62+
}
63+
int length = code.length();
64+
int lines = code.split("\n", -1).length;
65+
66+
// Get code snippet (first N characters for debugging)
67+
String snippet = getCodeSnippet(code, DEFAULT_CODE_SNIPPET_LENGTH);
68+
69+
return String.format(
70+
"[%s code, length: %d, lines: %d, snippet: %s]", codeType, length, lines, snippet);
71+
}
72+
73+
/**
74+
* Get code snippet (first N characters) for debugging
75+
*
76+
* @param code the code
77+
* @param length number of characters to show
78+
* @return code snippet (always truncated for security)
79+
*/
80+
public static String getCodeSnippet(String code, int length) {
81+
if (StringUtils.isBlank(code)) {
82+
return "";
83+
}
84+
85+
String trimmed = code.trim();
86+
87+
// Always truncate for security, even if code is short
88+
if (trimmed.length() <= length) {
89+
return trimmed + "...";
90+
}
91+
92+
return trimmed.substring(0, length) + "...";
93+
}
94+
95+
/**
96+
* Mask code for logging - shows type, length, line count, and preview
97+
*
98+
* @param code the code to mask
99+
* @param codeType the type of code (e.g., "SQL", "Scala", "Python")
100+
* @param maxPreviewLength maximum characters to preview
101+
* @return masked code information
102+
*/
103+
public static String maskCode(String code, String codeType, int maxPreviewLength) {
104+
if (StringUtils.isBlank(code)) {
105+
return "[empty " + codeType + " code]";
106+
}
107+
int length = code.length();
108+
int lines = code.split("\n", -1).length;
109+
110+
if (maxPreviewLength <= 0) {
111+
return String.format("[%s code, length: %d, lines: %d]", codeType, length, lines);
112+
}
113+
114+
String preview = getPreview(code, maxPreviewLength);
115+
return String.format(
116+
"[%s code, length: %d, lines: %d, preview: %s]", codeType, length, lines, preview);
117+
}
118+
119+
/**
120+
* Get a safe preview of the code (truncated and cleaned)
121+
*
122+
* @param code the code to preview
123+
* @param maxLength maximum length of preview
124+
* @return safe preview string
125+
*/
126+
public static String getPreview(String code, int maxLength) {
127+
if (StringUtils.isBlank(code)) {
128+
return "";
129+
}
130+
131+
// Remove sensitive patterns (passwords, tokens, etc.)
132+
String cleaned = removeSensitiveInfo(code);
133+
134+
// Truncate to max length
135+
if (cleaned.length() <= maxLength) {
136+
return cleaned;
137+
}
138+
139+
return cleaned.substring(0, maxLength) + "...";
140+
}
141+
142+
/**
143+
* Remove sensitive information from code preview
144+
*
145+
* @param code the code to clean
146+
* @return cleaned code
147+
*/
148+
private static String removeSensitiveInfo(String code) {
149+
// Remove common sensitive patterns
150+
String cleaned = code;
151+
152+
// Remove password values
153+
cleaned =
154+
cleaned.replaceAll(
155+
"(?i)(password|passwd|pwd)\\s*['\"]?\\s*[:=]\\s*['\"][^'\"]*['\"]", "$1='***'");
156+
cleaned = cleaned.replaceAll("(?i)(password|passwd|pwd)\\s*[:=]\\s*[^\\s'\"]+", "$1=***");
157+
158+
// Remove token/key values
159+
cleaned =
160+
cleaned.replaceAll(
161+
"(?i)(token|api[_-]?key|secret|access[_-]?key)\\s*['\"]?\\s*[:=]\\s*['\"][^'\"]*['\"]",
162+
"$1='***'");
163+
cleaned =
164+
cleaned.replaceAll(
165+
"(?i)(token|api[_-]?key|secret|access[_-]?key)\\s*[:=]\\s*[^\\s'\"]+", "$1=***");
166+
167+
// Remove connection strings (jdbc:, mysql:, postgres:, etc.)
168+
cleaned =
169+
cleaned.replaceAll("(?i)(jdbc:[^\\s'\"]+|mysql://[^\\s'\"]+|postgres://[^\\s'\"]+)", "***");
170+
171+
return cleaned;
172+
}
173+
174+
/**
175+
* Get only the first few lines of code (for preview)
176+
*
177+
* @param code the code to preview
178+
* @param maxLines maximum lines to show
179+
* @return preview string
180+
*/
181+
public static String getLinePreview(String code, int maxLines) {
182+
if (StringUtils.isBlank(code)) {
183+
return "";
184+
}
185+
186+
String[] lines = code.split("\n", -1);
187+
StringBuilder preview = new StringBuilder();
188+
189+
for (int i = 0; i < Math.min(maxLines, lines.length); i++) {
190+
if (i > 0) {
191+
preview.append("\n");
192+
}
193+
preview.append(lines[i]);
194+
}
195+
196+
if (lines.length > maxLines) {
197+
preview.append("\n... (").append(lines.length - maxLines).append(" more lines)");
198+
}
199+
200+
return preview.toString();
201+
}
202+
203+
/**
204+
* Get code type from file extension or code content
205+
*
206+
* @param code the code
207+
* @param fileType file extension (e.g., ".sql", ".scala", ".py")
208+
* @return detected code type
209+
*/
210+
public static String detectCodeType(String code, String fileType) {
211+
if (StringUtils.isNotBlank(fileType)) {
212+
switch (fileType.toLowerCase()) {
213+
case ".sql":
214+
return "SQL";
215+
case ".scala":
216+
return "Scala";
217+
case ".py":
218+
return "Python";
219+
case ".java":
220+
return "Java";
221+
case ".sh":
222+
case ".bash":
223+
return "Shell";
224+
case "hql":
225+
return "HiveQL";
226+
default:
227+
break;
228+
}
229+
}
230+
231+
// Try to detect from content
232+
if (StringUtils.isBlank(code)) {
233+
return "Unknown";
234+
}
235+
236+
String trimmed = code.trim().toUpperCase();
237+
238+
if (trimmed.startsWith("SELECT")
239+
|| trimmed.startsWith("INSERT")
240+
|| trimmed.startsWith("UPDATE")
241+
|| trimmed.startsWith("DELETE")
242+
|| trimmed.startsWith("CREATE")
243+
|| trimmed.startsWith("ALTER")
244+
|| trimmed.startsWith("DROP")
245+
|| trimmed.startsWith("WITH")) {
246+
return "SQL";
247+
}
248+
249+
if (trimmed.contains("DEF ") || trimmed.contains("CLASS ") || trimmed.startsWith("IMPORT ")) {
250+
if (code.contains("println") || code.contains("Array(")) {
251+
return "Scala";
252+
}
253+
return "Java";
254+
}
255+
256+
if (trimmed.startsWith("IMPORT ") || trimmed.contains("PRINT ") || trimmed.contains("DEF ")) {
257+
return "Python";
258+
}
259+
260+
return "Unknown";
261+
}
262+
}

linkis-commons/linkis-hadoop-common/src/main/scala/org/apache/linkis/hadoop/common/conf/HadoopConf.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ object HadoopConf {
7676
CommonVars("wds.linkis.hadoop.hdfs.cache.max.time", new TimeType("12h")).getValue.toLong
7777

7878
/**
79-
* Temporary directory for keytab files when LINKIS_KEYTAB_SWITCH is enabled
80-
* 默认使用系统临时目录下的 keytab 子目录
79+
* Temporary directory for keytab files when LINKIS_KEYTAB_SWITCH is enabled 默认使用系统临时目录下的 keytab
80+
* 子目录
8181
*/
8282
val KEYTAB_TEMP_DIR = CommonVars("linkis.keytab.temp.dir", "/tmp/keytab")
8383

linkis-commons/linkis-hadoop-common/src/main/scala/org/apache/linkis/hadoop/common/utils/HDFSUtils.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import org.apache.linkis.hadoop.common.conf.HadoopConf
2323
import org.apache.linkis.hadoop.common.conf.HadoopConf._
2424
import org.apache.linkis.hadoop.common.entity.HDFSFileSystemContainer
2525

26-
import com.google.common.cache.{CacheBuilder, LoadingCache, RemovalCause, RemovalListener, RemovalNotification}
2726
import org.apache.commons.io.IOUtils
2827
import org.apache.commons.lang3.StringUtils
2928
import org.apache.hadoop.conf.Configuration
@@ -40,6 +39,14 @@ import java.util.concurrent.atomic.AtomicLong
4039

4140
import scala.collection.JavaConverters._
4241

42+
import com.google.common.cache.{
43+
CacheBuilder,
44+
LoadingCache,
45+
RemovalCause,
46+
RemovalListener,
47+
RemovalNotification
48+
}
49+
4350
object HDFSUtils extends Logging {
4451

4552
private val fileSystemCache: java.util.Map[String, HDFSFileSystemContainer] =
@@ -52,9 +59,9 @@ object HDFSUtils extends Logging {
5259
val key = notification.getKey
5360
val path = notification.getValue
5461
val cause = notification.getCause
55-
62+
5663
logger.info(s"Keytab cache entry removed: $key, cause: $cause")
57-
64+
5865
// 当缓存项被移除时,清理对应的临时文件
5966
if (path != null) {
6067
val file = new File(path)
@@ -69,7 +76,8 @@ object HDFSUtils extends Logging {
6976
}
7077
}
7178

72-
CacheBuilder.newBuilder()
79+
CacheBuilder
80+
.newBuilder()
7381
.maximumSize(1000) // 最大缓存项数量
7482
.expireAfterAccess(24, TimeUnit.HOURS) // 24小时未访问过期
7583
.removalListener(removalListener)
@@ -525,7 +533,10 @@ object HDFSUtils extends Logging {
525533
// 确保keytab临时目录存在
526534
if (!Files.exists(keytabTempDir)) {
527535
Files.createDirectories(keytabTempDir)
528-
Files.setPosixFilePermissions(keytabTempDir, PosixFilePermissions.fromString("rwxr-xr-x"))
536+
Files.setPosixFilePermissions(
537+
keytabTempDir,
538+
PosixFilePermissions.fromString("rwxr-xr-x")
539+
)
529540
}
530541

531542
val cachedPath = keytabTempFileCache.getIfPresent(cacheKey)

0 commit comments

Comments
 (0)