修改名称。

This commit is contained in:
2025-11-07 09:22:39 +08:00
parent d9cf55b053
commit a8dcfbb6a7
2203 changed files with 3 additions and 4 deletions

View File

@@ -0,0 +1,206 @@
package com.xscm.moduleutil.utils.logger;
import androidx.annotation.Nullable;
/**
* SCore
* Created by ShangChuanliang
* on 2017/10/12.
*/
public final class DataLogger implements DataLoggingInterceptor.Logger {
private final StringBuilder mMessage = new StringBuilder();
public static class LogAdapter extends com.orhanobut.logger.AndroidLogAdapter {
private final boolean mDebuggable;
public LogAdapter(boolean debuggable) {
super();
mDebuggable = debuggable;
}
@Override
public boolean isLoggable(int priority, @Nullable String tag) {
// return super.isLoggable(priority, tag);
return mDebuggable;
}
}
public static class LogUtil {
public static void init(boolean debuggable) {
com.orhanobut.logger.Logger.addLogAdapter(new LogAdapter(debuggable));
}
public static void d(String message) {
com.orhanobut.logger.Logger.d(message);
}
public static void i(String message) {
com.orhanobut.logger.Logger.i(message);
}
public static void w(String message, Throwable e) {
String info = e != null ? e.toString() : "null";
com.orhanobut.logger.Logger.w(message + "" + info);
}
public static void e(String message, Throwable e) {
com.orhanobut.logger.Logger.e(e, message);
}
public static void json(String json) {
com.orhanobut.logger.Logger.json(json);
}
}
@Override
public void log(String message) {
// 请求或者响应开始
if (message.startsWith(DataLoggingInterceptor.sLogStartFlag)) {
mMessage.setLength(0);
return;
}
// 响应结束,打印整条日志
if (message.startsWith(DataLoggingInterceptor.sLogEndFlag)) {
LogUtil.d(mMessage.toString());
return;
}
// 以{}或者[]形式的说明是响应结果的json数据需要进行格式化
if ((message.startsWith("{") && message.endsWith("}"))
|| (message.startsWith("[") && message.endsWith("]"))) {
message = formatJson(decodeUnicode(message));
}
// 追加消息
try { mMessage.append(message.concat("\n"));
} catch (Exception ignored) {}
}
@Override
public void reset() {
mMessage.setLength(0);
}
private static String formatJson(String jsonStr) {
if (null == jsonStr || "".equals(jsonStr)) return "";
StringBuilder sb = new StringBuilder();
char last = '\0';
char current = '\0';
int indent = 0;
for (int i = 0; i < jsonStr.length(); i++) {
last = current;
current = jsonStr.charAt(i);
//遇到{ [换行,且下一行缩进
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]换行,当前行缩进
case '}':
case ']':
sb.append('\n');
indent--;
addIndentBlank(sb, indent);
sb.append(current);
break;
//遇到,换行
case ',':
sb.append(current);
if (last != '\\') {
sb.append('\n');
addIndentBlank(sb, indent);
}
break;
default:
sb.append(current);
}
}
return sb.toString();
}
/**
* 添加space
*
* @param sb
* @param indent
*/
private static void addIndentBlank(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
}
/**
* http 请求数据返回 json 中中文字符为 unicode 编码转汉字转码
*
* @param theString
* @return 转化后的结果.
*/
private static String decodeUnicode(String theString) {
char aChar;
int len = theString.length();
StringBuilder outBuffer = new StringBuilder(len);
for (int x = 0; x < len; ) {
aChar = theString.charAt(x++);
if (aChar == '\\') {
aChar = theString.charAt(x++);
if (aChar == 'u') {
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = theString.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't')
aChar = '\t';
else if (aChar == 'r')
aChar = '\r';
else if (aChar == 'n')
aChar = '\n';
else if (aChar == 'f')
aChar = '\f';
outBuffer.append(aChar);
}
} else
outBuffer.append(aChar);
}
return outBuffer.toString();
}
}

View File

@@ -0,0 +1,215 @@
package com.xscm.moduleutil.utils.logger;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.io.EOFException;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import okio.BufferedSource;
/**
* SCore
* Created by ShangChuanliang
* on 2017/12/21.
*/
public class DataLoggingInterceptor implements Interceptor {
private static final String sFormatLine =
"===========================================================================================";
public static final String sLogStartFlag = "==scliang==log==start==";
public static final String sLogEndFlag = "==scliang==log==end==";
private static final Charset UTF8 = StandardCharsets.UTF_8;
public interface Logger {
void reset();
void log(String message);
}
public DataLoggingInterceptor(Logger logger) {
this.logger = logger;
}
private final Logger logger;
@NotNull
@Override public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
logger.reset();
logger.log(sLogStartFlag);
logger.log(sFormatLine);
RequestBody requestBody = request.body();
boolean hasRequestBody = requestBody != null;
Connection connection = chain.connection();
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
String requestStartMessage = request.method() + " " + request.url() + " " + protocol;
logger.log(requestStartMessage);
if (hasRequestBody) {
// Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
if (requestBody.contentType() != null) {
logger.log("Content-Type: " + requestBody.contentType());
}
if (requestBody.contentLength() != -1) {
logger.log("Content-Length: " + requestBody.contentLength());
}
}
Headers rHeaders = request.headers();
for (int i = 0, count = rHeaders.size(); i < count; i++) {
String name = rHeaders.name(i);
// Skip headers from the request body as they are explicitly logged above.
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
logger.log(name + ": " + rHeaders.value(i));
}
}
if (!hasRequestBody) {
logger.log("END " + request.method());
} else if (bodyEncoded(request.headers())) {
logger.log("END " + request.method() + " (encoded body omitted)");
} else {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
if (charset != null) {
logger.log(sFormatLine);
if (isPlaintext(buffer)) {
try {
String requestStr = URLDecoder.decode(buffer.readString(charset), "UTF-8");
String[] strs = requestStr.split("&");
for (String str : strs) {
logger.log(str);
}
} catch (Exception e) {
logger.log(buffer.readString(charset));
}
logger.log("END " + request.method()
+ " (" + requestBody.contentLength() + "-byte body)");
} else {
logger.log("END " + request.method() + " (binary "
+ requestBody.contentLength() + "-byte body omitted)");
}
}
}
logger.log(sFormatLine);
long startNs = System.nanoTime();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
logger.log("HTTP FAILED: " + e);
logger.log(sLogEndFlag);
throw e;
}
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
ResponseBody responseBody = response.body();
if (responseBody != null) {
long contentLength = responseBody.contentLength();
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
logger.log(response.code() + " " + response.message() + " "
+ response.request().url() + " (" + tookMs + "ms)");
Headers headers = response.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
logger.log(headers.name(i) + ": " + headers.value(i));
}
if (!HttpHeaders.hasBody(response)) {
logger.log("END HTTP");
} else if (bodyEncoded(response.headers())) {
logger.log("END HTTP (encoded body omitted)");
} else {
BufferedSource source = responseBody.source();
if (source.isOpen()) {
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
if (!isPlaintext(buffer)) {
logger.log("END HTTP (binary " + buffer.size() + "-byte body omitted)");
logger.log(sFormatLine);
logger.log(sLogEndFlag);
return response;
}
if (contentLength != 0 && charset != null) {
logger.log(sFormatLine);
logger.log(buffer.clone().readString(charset));
}
logger.log("END HTTP (" + buffer.size() + "-byte body)");
} else {
logger.log("END HTTP");
}
}
}
logger.log(sFormatLine);
logger.log(sLogEndFlag);
return response;
}
/**
* Returns true if the body in question probably contains human readable text. Uses a small sample
* of code points to detect unicode control characters commonly used in binary file signatures.
*/
private static boolean isPlaintext(Buffer buffer) {
try {
Buffer prefix = new Buffer();
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
buffer.copyTo(prefix, 0, byteCount);
for (int i = 0; i < 16; i++) {
if (prefix.exhausted()) {
break;
}
int codePoint = prefix.readUtf8CodePoint();
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false;
}
}
return true;
} catch (EOFException e) {
return false; // Truncated UTF-8 sequence.
}
}
private boolean bodyEncoded(Headers headers) {
String contentEncoding = headers.get("Content-Encoding");
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
}
}

View File

@@ -0,0 +1,276 @@
package com.xscm.moduleutil.utils.logger;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import okio.BufferedSource;
/**
* OkHttp 日志拦截器,用于打印请求和响应详情
*/
public class LogInterceptor implements Interceptor {
private static final String TAG = "NetworkLog";
private static final Charset UTF8 = StandardCharsets.UTF_8;
// 日志开关可根据debug/release环境动态设置
private boolean isLogEnabled = true;
// 是否打印请求体
private boolean logRequestBody = true;
// 是否打印响应体
private boolean logResponseBody = true;
// 最大日志长度(避免过大的响应体导致日志刷屏)
private int maxLogLength = 2048;
public LogInterceptor() {
}
// 配置方法
public LogInterceptor setLogEnabled(boolean enabled) {
isLogEnabled = enabled;
return this;
}
public LogInterceptor setLogRequestBody(boolean logRequestBody) {
this.logRequestBody = logRequestBody;
return this;
}
public LogInterceptor setLogResponseBody(boolean logResponseBody) {
this.logResponseBody = logResponseBody;
return this;
}
public LogInterceptor setMaxLogLength(int maxLogLength) {
this.maxLogLength = maxLogLength;
return this;
}
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
if (!isLogEnabled) {
return chain.proceed(chain.request());
}
Request request = chain.request();
// 打印请求日志
logRequest(request);
// 记录请求开始时间,用于计算耗时
long startNs = System.nanoTime();
Response response;
try {
response = chain.proceed(request);
} catch (Exception e) {
// 打印请求异常
Log.e(TAG, "请求失败: " + e.getMessage());
throw e;
}
// 计算请求耗时
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
// 打印响应日志
logResponse(response, tookMs);
return response;
}
/**
* 打印请求日志
*/
private void logRequest(Request request) {
try {
StringBuilder log = new StringBuilder();
log.append("\n==================== 请求开始 ====================\n");
// 请求行: 方法 + URL
log.append(String.format("方法: %s URL: %s\n", request.method(), request.url()));
// 请求头
log.append("请求头:\n");
for (String name : request.headers().names()) {
// 脱敏敏感头信息如Authorization、Cookie等
String value = isSensitiveHeader(name) ? "***" : request.headers().get(name);
log.append(String.format(" %s: %s\n", name, value));
}
// 请求体
if (logRequestBody && request.body() != null) {
RequestBody requestBody = request.body();
if (requestBody.contentLength() > 0) {
log.append("请求体:\n");
// 复制请求体(避免原请求体被消耗)
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
// 读取请求体内容
String body = buffer.readString(charset);
// 格式化JSON如果是JSON类型
if (isJson(contentType)) {
body = formatJson(body);
}
// 截断过长的日志
log.append(truncateLog(body)).append("\n");
}
}
log.append("==================== 请求结束 ====================\n");
Log.d(TAG, log.toString());
} catch (Exception e) {
Log.e(TAG, "打印请求日志失败: " + e.getMessage());
}
}
/**
* 打印响应日志
*/
private void logResponse(Response response, long tookMs) {
try {
StringBuilder log = new StringBuilder();
log.append("\n==================== 响应开始 ====================\n");
// 响应行: 状态码 + 消息 + 耗时
log.append(String.format("状态码: %d 消息: %s 耗时: %dms\n",
response.code(), response.message(), tookMs));
// 响应头
log.append("响应头:\n");
for (String name : response.headers().names()) {
log.append(String.format(" %s: %s\n", name, response.headers().get(name)));
}
// 响应体
if (logResponseBody && HttpHeaders.hasBody(response)) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // 读取整个响应体
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
// 读取响应体内容
String body = buffer.clone().readString(charset);
// 格式化JSON
if (isJson(contentType)) {
body = formatJson(body);
}
// 截断过长的日志
log.append("响应体:\n").append(truncateLog(body)).append("\n");
}
}
log.append("==================== 响应结束 ====================\n");
Log.d(TAG, log.toString());
} catch (Exception e) {
Log.e(TAG, "打印响应日志失败: " + e.getMessage());
}
}
/**
* 判断是否为JSON类型
*/
private boolean isJson(MediaType mediaType) {
if (mediaType == null) return false;
return mediaType.type().equals("application") &&
mediaType.subtype().equals("json");
}
/**
* 格式化JSON字符串增强可读性
*/
private String formatJson(String json) {
try {
// 简单格式化可根据需要使用更复杂的JSON格式化库
StringBuilder formatted = new StringBuilder();
int indent = 0;
boolean inQuotes = false;
char lastChar = ' ';
for (char c : json.toCharArray()) {
if (c == '"' && lastChar != '\\') {
inQuotes = !inQuotes;
}
if (!inQuotes) {
switch (c) {
case '{':
case '[':
formatted.append(c).append("\n");
indent += 4;
formatted.append(" ".repeat(indent));
break;
case '}':
case ']':
formatted.append("\n");
indent -= 4;
formatted.append(" ".repeat(indent)).append(c);
break;
case ',':
formatted.append(c).append("\n").append(" ".repeat(indent));
break;
case ':':
formatted.append(" : ");
break;
default:
formatted.append(c);
break;
}
} else {
formatted.append(c);
}
lastChar = c;
}
return formatted.toString();
} catch (Exception e) {
// 格式化失败时返回原始字符串
return json;
}
}
/**
* 截断过长的日志
*/
private String truncateLog(String log) {
if (log.length() <= maxLogLength) {
return log;
}
return log.substring(0, maxLogLength) + "\n...[日志过长,已截断]...";
}
/**
* 判断是否为敏感头信息(需要脱敏)
*/
private boolean isSensitiveHeader(String headerName) {
String lowerHeader = headerName.toLowerCase();
return lowerHeader.contains("authorization") ||
lowerHeader.contains("cookie") ||
lowerHeader.contains("token") ||
lowerHeader.contains("secret");
}
}

View File

@@ -0,0 +1,112 @@
package com.xscm.moduleutil.utils.logger;
import android.util.Log;
import java.lang.ref.SoftReference;
/**
* SCore
* Created by ShangChuanliang
* on 2017/9/29.
*/
public class Logger {
public interface OnLoggerListener {
void onLogI(String buildType, String tag, String msg);
void onLogD(String buildType, String tag, String msg);
void onLogE(String buildType, String tag, String msg);
void onLogV(String buildType, String tag, String msg);
void onLogW(String buildType, String tag, String msg);
}
private Logger() {}
private static SoftReference<OnLoggerListener> sOnLoggerListener;
private static final String Tag = "SLog";
private static String BuildType;
private static boolean IsRelease;
private static boolean IsBeta;
private static boolean Loggable;
public static void init(String buildType) {
BuildType = buildType;
IsRelease = "release".equalsIgnoreCase(BuildType);
IsBeta = "beta".equalsIgnoreCase(BuildType);
Loggable = !IsRelease;
}
public static void setOnLoggerListener(OnLoggerListener listener) {
sOnLoggerListener = new SoftReference<>(listener);
}
public static String getBuildType() {
return BuildType;
}
public static boolean isRelease() {
return IsRelease;
}
public static boolean isBeta() {
return IsBeta;
}
public static boolean isLoggable() {
return Loggable;
}
public static void i(String msg, Object... args) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) if (arg != null) sb.append(";").append(arg);
callOnLoggerListener(1, BuildType, Tag, msg + sb.toString());
if (isLoggable()) Log.i(Tag, msg + sb.toString());
}
public static void d(String msg, Object... args) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) if (arg != null) sb.append(";").append(arg);
callOnLoggerListener(2, BuildType, Tag, msg + sb.toString());
if (isLoggable()) Log.d(Tag, msg + sb.toString());
}
public static void e(String msg, Object... args) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) if (arg != null) sb.append(";").append(arg);
callOnLoggerListener(3, BuildType, Tag, msg + sb.toString());
if (isLoggable()) Log.e(Tag, msg + sb.toString());
}
public static void v(String msg, Object... args) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) if (arg != null) sb.append(";").append(arg);
callOnLoggerListener(4, BuildType, Tag, msg + sb.toString());
if (isLoggable()) Log.v(Tag, msg + sb.toString());
}
public static void w(String msg, Object... args) {
StringBuilder sb = new StringBuilder();
for (Object arg : args) if (arg != null) sb.append(";").append(arg);
callOnLoggerListener(5, BuildType, Tag, msg + sb.toString());
if (isLoggable()) Log.w(Tag, msg + sb.toString());
}
private static void callOnLoggerListener(int type, String buildType, String tag, String msg) {
if (sOnLoggerListener == null) {
return;
}
OnLoggerListener listener = sOnLoggerListener.get();
if (listener == null) {
return;
}
switch (type) {
case 1: listener.onLogI(buildType, tag, msg); break;
case 2: listener.onLogD(buildType, tag, msg); break;
case 3: listener.onLogE(buildType, tag, msg); break;
case 4: listener.onLogV(buildType, tag, msg); break;
case 5: listener.onLogW(buildType, tag, msg); break;
}
}
}