mirror of
https://github.com/M66B/FairEmail.git
synced 2025-03-15 16:39:37 +00:00
Replaced logcat by TinyLog
This commit is contained in:
parent
5b96003259
commit
37dea4eb32
14 changed files with 205 additions and 70 deletions
|
@ -50,3 +50,4 @@ FairEmail uses parts or all of:
|
|||
* [Liberation Sans Narrow font](https://github.com/liberationfonts/liberation-sans-narrow). Copyright (C) 1989, 1991 Free Software Foundation, Inc. [GNU General Public License version 2 with exceptions](https://fedoraproject.org/wiki/Licensing/LiberationFontLicense).
|
||||
* [Prism](https://github.com/PrismJS/prism). Copyright (c) 2012 Lea Verou. [MIT LICENSE](https://github.com/PrismJS/prism/blob/master/LICENSE).
|
||||
* [Jayway JsonPath](https://github.com/json-path/JsonPath). Copyright 2011 the original author or authors. [Apache License 2.0](https://github.com/json-path/JsonPath/blob/master/LICENSE).
|
||||
* [tinylog 2](https://github.com/tinylog-org/tinylog). Copyright 2016-2023 Martin Winandy. [Apache License 2.0](https://github.com/tinylog-org/tinylog/blob/v2.7/license.txt).
|
||||
|
|
|
@ -545,6 +545,7 @@ dependencies {
|
|||
def ipaddress_version = "5.4.0"
|
||||
def canary_version = "2.12"
|
||||
def ws_version = "2.14"
|
||||
def tinylog_version = "2.6.2"
|
||||
|
||||
// https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs?repo=google
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
@ -790,4 +791,9 @@ dependencies {
|
|||
|
||||
// https://github.com/TakahikoKawasaki/nv-websocket-client
|
||||
implementation "com.neovisionaries:nv-websocket-client:$ws_version"
|
||||
|
||||
// https://tinylog.org/v2/download/
|
||||
// https://mvnrepository.com/artifact/org.tinylog
|
||||
implementation "org.tinylog:tinylog-api:$tinylog_version"
|
||||
implementation "org.tinylog:tinylog-impl:$tinylog_version"
|
||||
}
|
||||
|
|
12
app/proguard-rules.pro
vendored
12
app/proguard-rules.pro
vendored
|
@ -150,4 +150,14 @@
|
|||
|
||||
#ShortcutBadger
|
||||
-keep class me.leolin.shortcutbadger.** {*;}
|
||||
-keepnames class me.leolin.shortcutbadger.** {*;}
|
||||
-keepnames class me.leolin.shortcutbadger.** {*;}
|
||||
|
||||
#https://tinylog.org/v2/configuration/#proguard
|
||||
-keepnames interface org.tinylog.**
|
||||
-keepnames class * implements org.tinylog.**
|
||||
-keepclassmembers class * implements org.tinylog.** { <init>(...); }
|
||||
|
||||
-dontwarn dalvik.system.VMStack
|
||||
-dontwarn java.lang.**
|
||||
-dontwarn javax.naming.**
|
||||
-dontwarn sun.reflect.Reflection
|
||||
|
|
|
@ -50,3 +50,4 @@ FairEmail uses parts or all of:
|
|||
* [Liberation Sans Narrow font](https://github.com/liberationfonts/liberation-sans-narrow). Copyright (C) 1989, 1991 Free Software Foundation, Inc. [GNU General Public License version 2 with exceptions](https://fedoraproject.org/wiki/Licensing/LiberationFontLicense).
|
||||
* [Prism](https://github.com/PrismJS/prism). Copyright (c) 2012 Lea Verou. [MIT LICENSE](https://github.com/PrismJS/prism/blob/master/LICENSE).
|
||||
* [Jayway JsonPath](https://github.com/json-path/JsonPath). Copyright 2011 the original author or authors. [Apache License 2.0](https://github.com/json-path/JsonPath/blob/master/LICENSE).
|
||||
* [tinylog 2](https://github.com/tinylog-org/tinylog). Copyright 2016-2023 Martin Winandy. [Apache License 2.0](https://github.com/tinylog-org/tinylog/blob/v2.7/license.txt).
|
||||
|
|
|
@ -1628,8 +1628,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
tvError.setVisibility(View.VISIBLE);
|
||||
ibError.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (BuildConfig.DEBUG && Log.isDebugLogLevel())
|
||||
error = message.thread;
|
||||
tvError.setText(error);
|
||||
tvError.setVisibility(error == null ? View.GONE : View.VISIBLE);
|
||||
ibError.setVisibility(error == null ? View.GONE : View.VISIBLE);
|
||||
|
|
|
@ -67,6 +67,7 @@ public class ApplicationEx extends Application
|
|||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
TinyLogConfigurationLoader.setup(base);
|
||||
super.attachBaseContext(getLocalizedContext(base));
|
||||
}
|
||||
|
||||
|
@ -401,10 +402,6 @@ public class ApplicationEx extends Application
|
|||
// Should be excluded for import
|
||||
restart(this, key);
|
||||
break;
|
||||
case "debug":
|
||||
case "log_level":
|
||||
Log.setLevel(this);
|
||||
break;
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
|
|
|
@ -3131,7 +3131,7 @@ class Core {
|
|||
private static void onDownload(Context context, JSONArray jargs, EntityAccount account, EntityFolder folder, EntityMessage message, IMAPStore istore, IMAPFolder ifolder, State state) throws MessagingException, IOException, JSONException {
|
||||
long uid = jargs.getLong(0);
|
||||
if (!Objects.equals(uid, message.uid))
|
||||
throw new IllegalArgumentException("Different uid" + uid + "/" + message.uid);
|
||||
throw new IllegalArgumentException("Different uid=" + uid + "/" + message.uid);
|
||||
|
||||
MimeMessage imessage = (MimeMessage) ifolder.getMessageByUID(uid);
|
||||
downloadMessage(context, account, folder, istore, ifolder, imessage, message.id, state, new SyncStats());
|
||||
|
|
|
@ -774,7 +774,7 @@ public class EmailService implements AutoCloseable {
|
|||
|
||||
breadcrumbs = new RingBuffer<>(BREADCRUMBS_SIZE);
|
||||
|
||||
boolean trace = (debug || log || Log.isDebugLogLevel());
|
||||
boolean trace = (debug || log);
|
||||
|
||||
isession.setDebug(trace);
|
||||
if (trace)
|
||||
|
|
|
@ -375,18 +375,41 @@ public class EntityAttachment {
|
|||
File file = getFile(context);
|
||||
File zip = new File(file.getAbsolutePath() + ".zip");
|
||||
|
||||
try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
|
||||
try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zip)))) {
|
||||
out.setMethod(ZipOutputStream.DEFLATED);
|
||||
out.setLevel(Deflater.BEST_COMPRESSION);
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
out.putNextEntry(entry);
|
||||
try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zip)))) {
|
||||
out.setMethod(ZipOutputStream.DEFLATED);
|
||||
out.setLevel(Deflater.BEST_COMPRESSION);
|
||||
ZipEntry entry = new ZipEntry(name);
|
||||
out.putNextEntry(entry);
|
||||
try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
|
||||
Helper.copy(in, out);
|
||||
}
|
||||
}
|
||||
|
||||
DB db = DB.getInstance(context);
|
||||
db.attachment().setName(id, name + ".zip", "application/zip", zip.length());
|
||||
db.attachment().setDownloaded(id, zip.length());
|
||||
Helper.secureDelete(file);
|
||||
}
|
||||
|
||||
void zip(Context context, File[] files) throws IOException {
|
||||
File file = getFile(context);
|
||||
File zip = new File(file.getAbsolutePath() + ".zip");
|
||||
|
||||
try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zip)))) {
|
||||
out.setMethod(ZipOutputStream.DEFLATED);
|
||||
out.setLevel(Deflater.BEST_COMPRESSION);
|
||||
for (File f : files) {
|
||||
ZipEntry entry = new ZipEntry(f.getName());
|
||||
out.putNextEntry(entry);
|
||||
try (InputStream in = new BufferedInputStream(new FileInputStream(f))) {
|
||||
Helper.copy(in, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DB db = DB.getInstance(context);
|
||||
db.attachment().setName(id, name + ".zip", "application/zip", zip.length());
|
||||
db.attachment().setDownloaded(id, zip.length());
|
||||
Helper.secureDelete(file);
|
||||
}
|
||||
|
||||
|
|
|
@ -892,7 +892,6 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
|
|||
if (checked)
|
||||
prefs.edit()
|
||||
.putLong("protocol_since", new Date().getTime())
|
||||
.putInt("log_level", android.util.Log.INFO)
|
||||
.apply();
|
||||
else
|
||||
EntityLog.clear(compoundButton.getContext());
|
||||
|
@ -901,8 +900,9 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
|
|||
|
||||
swLogInfo.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
prefs.edit().putInt("log_level", checked ? android.util.Log.INFO : android.util.Log.WARN).apply();
|
||||
public void onCheckedChanged(CompoundButton v, boolean checked) {
|
||||
prefs.edit().putInt("log_level", checked ? android.util.Log.INFO : android.util.Log.WARN).commit();
|
||||
ApplicationEx.restart(v.getContext(), "log_level");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -195,7 +195,6 @@ import javax.net.ssl.X509TrustManager;
|
|||
public class Log {
|
||||
private static Context ctx;
|
||||
|
||||
private static int level = android.util.Log.INFO;
|
||||
private static final long MAX_LOG_SIZE = 8 * 1024 * 1024L;
|
||||
private static final int MAX_CRASH_REPORTS = (BuildConfig.TEST_RELEASE ? 50 : 5);
|
||||
private static final long MIN_FILE_SIZE = 1024 * 1024L;
|
||||
|
@ -244,52 +243,27 @@ public class Log {
|
|||
|
||||
public static native long[] jni_safe_runtime_stats();
|
||||
|
||||
public static void setLevel(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean debug = prefs.getBoolean("debug", false);
|
||||
if (debug)
|
||||
level = android.util.Log.DEBUG;
|
||||
else {
|
||||
int def = (BuildConfig.DEBUG ? android.util.Log.INFO : android.util.Log.WARN);
|
||||
level = prefs.getInt("log_level", def);
|
||||
}
|
||||
jni_safe_log(android.util.Log.DEBUG, TAG, "Log level=" + level);
|
||||
}
|
||||
|
||||
public static boolean isDebugLogLevel() {
|
||||
return (level <= android.util.Log.INFO);
|
||||
}
|
||||
|
||||
public static int d(String msg) {
|
||||
if (level <= android.util.Log.DEBUG)
|
||||
return jni_safe_log(android.util.Log.DEBUG, TAG, msg);
|
||||
else
|
||||
return 0;
|
||||
return d(TAG, msg);
|
||||
}
|
||||
|
||||
public static int d(String tag, String msg) {
|
||||
if (level <= android.util.Log.DEBUG)
|
||||
return jni_safe_log(android.util.Log.DEBUG, tag, msg);
|
||||
else
|
||||
return 0;
|
||||
org.tinylog.Logger.tag(tag).debug(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int i(String msg) {
|
||||
if (level <= android.util.Log.INFO || BuildConfig.DEBUG || BuildConfig.TEST_RELEASE)
|
||||
return jni_safe_log(android.util.Log.INFO, TAG, msg);
|
||||
else
|
||||
return 0;
|
||||
return i(TAG, msg);
|
||||
}
|
||||
|
||||
public static int i(String tag, String msg) {
|
||||
if (level <= android.util.Log.INFO || BuildConfig.DEBUG || BuildConfig.TEST_RELEASE)
|
||||
return jni_safe_log(android.util.Log.INFO, TAG, msg);
|
||||
else
|
||||
return 0;
|
||||
org.tinylog.Logger.tag(tag).info(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int w(String msg) {
|
||||
return jni_safe_log(android.util.Log.WARN, TAG, msg);
|
||||
org.tinylog.Logger.tag(TAG).warn(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int e(String msg) {
|
||||
|
@ -307,11 +281,14 @@ public class Log {
|
|||
} catch (Throwable ex) {
|
||||
Log.i(ex);
|
||||
}
|
||||
return jni_safe_log(android.util.Log.ERROR, TAG, msg);
|
||||
|
||||
org.tinylog.Logger.tag(TAG).error(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int i(Throwable ex) {
|
||||
return jni_safe_log(android.util.Log.INFO, TAG, getDescription(ex));
|
||||
org.tinylog.Logger.tag(TAG).info(ex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int w(Throwable ex) {
|
||||
|
@ -330,7 +307,9 @@ public class Log {
|
|||
} catch (Throwable ex1) {
|
||||
Log.i(ex1);
|
||||
}
|
||||
return jni_safe_log(android.util.Log.WARN, TAG, getDescription(ex));
|
||||
|
||||
org.tinylog.Logger.tag(TAG).warn(ex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int e(Throwable ex) {
|
||||
|
@ -349,11 +328,14 @@ public class Log {
|
|||
} catch (Throwable ex1) {
|
||||
Log.i(ex1);
|
||||
}
|
||||
return jni_safe_log(android.util.Log.ERROR, TAG, getDescription(ex));
|
||||
|
||||
org.tinylog.Logger.tag(TAG).error(ex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int i(String prefix, Throwable ex) {
|
||||
return jni_safe_log(android.util.Log.INFO, TAG, prefix + " " + getDescription(ex));
|
||||
org.tinylog.Logger.tag(TAG).info(ex, prefix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int w(String prefix, Throwable ex) {
|
||||
|
@ -369,7 +351,9 @@ public class Log {
|
|||
} catch (Throwable ex1) {
|
||||
Log.i(ex1);
|
||||
}
|
||||
return jni_safe_log(android.util.Log.WARN, TAG, prefix + " " + getDescription(ex));
|
||||
|
||||
org.tinylog.Logger.tag(TAG).warn(ex, prefix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int e(String prefix, Throwable ex) {
|
||||
|
@ -385,24 +369,21 @@ public class Log {
|
|||
} catch (Throwable ex1) {
|
||||
Log.i(ex1);
|
||||
}
|
||||
return jni_safe_log(android.util.Log.ERROR, TAG, prefix + " " + getDescription(ex));
|
||||
|
||||
org.tinylog.Logger.tag(TAG).error(ex, prefix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void persist(String message) {
|
||||
if (ctx == null)
|
||||
Log.e(message);
|
||||
org.tinylog.Logger.tag(TAG).error(message);
|
||||
else
|
||||
EntityLog.log(ctx, message);
|
||||
}
|
||||
|
||||
private static String getDescription(Throwable ex) {
|
||||
ThrowableWrapper t = new ThrowableWrapper(ex);
|
||||
return t.toSafeString() + "\n" + t.getSafeStackTraceString();
|
||||
}
|
||||
|
||||
public static void persist(EntityLog.Type type, String message) {
|
||||
if (ctx == null)
|
||||
Log.e(message);
|
||||
org.tinylog.Logger.tag(TAG).error(type.name() + " " + message);
|
||||
else
|
||||
EntityLog.log(ctx, type, message);
|
||||
}
|
||||
|
@ -470,7 +451,6 @@ public class Log {
|
|||
|
||||
static void setup(Context context) {
|
||||
ctx = context;
|
||||
setLevel(context);
|
||||
setupBugsnag(context);
|
||||
}
|
||||
|
||||
|
@ -2197,8 +2177,8 @@ public class Log {
|
|||
}
|
||||
}
|
||||
|
||||
sb.append(String.format("Log main: %b protocol: %b debug: %b build: %b test: %b\r\n",
|
||||
main_log, protocol, Log.isDebugLogLevel(), BuildConfig.DEBUG, BuildConfig.TEST_RELEASE));
|
||||
sb.append(String.format("Log main: %b protocol: %b build: %b test: %b\r\n",
|
||||
main_log, protocol, BuildConfig.DEBUG, BuildConfig.TEST_RELEASE));
|
||||
|
||||
int[] contacts = ContactInfo.getStats();
|
||||
sb.append(String.format("Contact lookup: %d cached: %d\r\n",
|
||||
|
@ -3153,6 +3133,8 @@ public class Log {
|
|||
attachment.progress = 0;
|
||||
attachment.id = db.attachment().insertAttachment(attachment);
|
||||
|
||||
attachment.zip(context, TinyLogConfigurationLoader.getFiles(context));
|
||||
/*
|
||||
// https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html#java
|
||||
ProcessBuilder pb = new ProcessBuilder("/system/bin/logcat",
|
||||
"-d",
|
||||
|
@ -3182,6 +3164,7 @@ public class Log {
|
|||
if (proc != null)
|
||||
proc.destroy();
|
||||
}
|
||||
*/
|
||||
} catch (Throwable ex) {
|
||||
Log.e(ex);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package eu.faircode.email;
|
||||
|
||||
/*
|
||||
This file is part of FairEmail.
|
||||
|
||||
FairEmail is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
FairEmail is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2018-2023 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.tinylog.Level;
|
||||
import org.tinylog.configuration.PropertiesConfigurationLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Properties;
|
||||
|
||||
// https://tinylog.org/v2/configuration/
|
||||
// https://github.com/tinylog-org/tinylog-android-example/blob/v2/app/src/main/resources/tinylog.properties
|
||||
|
||||
public class TinyLogConfigurationLoader extends PropertiesConfigurationLoader {
|
||||
private static Level level = Level.TRACE;
|
||||
|
||||
@Override
|
||||
public Properties load() {
|
||||
Properties props = super.load();
|
||||
props.setProperty("level", level.name());
|
||||
return props;
|
||||
}
|
||||
|
||||
static void setup(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean debug = prefs.getBoolean("debug", false); // Changing requires force stop
|
||||
|
||||
if (debug)
|
||||
level = Level.DEBUG;
|
||||
else {
|
||||
int def = (BuildConfig.DEBUG || BuildConfig.TEST_RELEASE ? android.util.Log.INFO : android.util.Log.WARN);
|
||||
int _level = prefs.getInt("log_level", def);
|
||||
if (_level == android.util.Log.VERBOSE)
|
||||
level = Level.TRACE;
|
||||
else if (_level == android.util.Log.DEBUG)
|
||||
level = Level.DEBUG;
|
||||
else if (_level == android.util.Log.INFO)
|
||||
level = Level.INFO;
|
||||
else if (_level == android.util.Log.WARN)
|
||||
level = Level.WARN;
|
||||
else if (_level == android.util.Log.ERROR)
|
||||
level = Level.ERROR;
|
||||
}
|
||||
|
||||
System.setProperty("tinylog.configurationloader",
|
||||
TinyLogConfigurationLoader.class.getName());
|
||||
|
||||
System.setProperty("tinylog.directory",
|
||||
new File(context.getFilesDir(), "logs").getAbsolutePath());
|
||||
}
|
||||
|
||||
static File[] getFiles(Context context) {
|
||||
File[] files = new File(context.getFilesDir(), "logs").listFiles();
|
||||
|
||||
if (files == null)
|
||||
return new File[0];
|
||||
|
||||
Arrays.sort(files, new Comparator<File>() {
|
||||
@Override
|
||||
public int compare(File f1, File f2) {
|
||||
return f1.getName().compareTo(f2.getName());
|
||||
}
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
|
@ -784,6 +784,19 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/tvProtocolHint"
|
||||
app:switchPadding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLogInfoHint"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:text="@string/title_advanced_english_hint"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/swLogInfo" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/swDebug"
|
||||
android:layout_width="0dp"
|
||||
|
@ -794,7 +807,7 @@
|
|||
android:text="@string/title_advanced_debug"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/swLogInfo"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvLogInfoHint"
|
||||
app:switchPadding="12dp" />
|
||||
|
||||
<TextView
|
||||
|
|
12
app/src/main/resources/tinylog.properties
Normal file
12
app/src/main/resources/tinylog.properties
Normal file
|
@ -0,0 +1,12 @@
|
|||
level = warn
|
||||
|
||||
writer1 = logcat
|
||||
writer1.format = {tag} {message}
|
||||
|
||||
writer2 = rolling file
|
||||
writer2.file = #{tinylog.directory}/{date:yyyy-MM-dd_HH-mm-ss}.txt
|
||||
writer2.format = {date: HH:mm:ss.SSS} {tag} {level}: {message}
|
||||
writer2.policies = size: 1mb
|
||||
writer2.backups = 10
|
||||
|
||||
writingthread = true
|
Loading…
Add table
Reference in a new issue