Skip to content

Commit c67a71f

Browse files
committed
[mixin_logger] support custom file content leading
1 parent 0eeeb80 commit c67a71f

File tree

5 files changed

+107
-19
lines changed

5 files changed

+107
-19
lines changed

packages/mixin_logger/lib/mixin_logger.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,24 @@ extension _LogLevelExtension on _LogLevel {
6464
}
6565
}
6666

67+
///
68+
/// Init logger to write log to file.
69+
///
70+
/// [logDir] the directory to store log files.
71+
/// [fileLeading] the leading of log file content, it will be written
72+
/// to the first line of each log file.
6773
Future<void> initLogger(
6874
String logDir, {
6975
int maxFileCount = 10,
7076
int maxFileLength = 1024 * 1024 * 10, // 10 MB
77+
String? fileLeading,
7178
}) async {
7279
assert(maxFileCount > 1, 'maxFileCount must be greater than 1');
7380
assert(maxFileLength > 10 * 1024, 'maxFileLength must be greater than 10 KB');
74-
await platform.initLogger(logDir, maxFileCount, maxFileLength);
81+
if (fileLeading != null) {
82+
assert(fileLeading.length < maxFileLength, 'fileLeading is too long');
83+
}
84+
await platform.initLogger(logDir, maxFileCount, maxFileLength, fileLeading);
7585
}
7686

7787
void v(String message) {

packages/mixin_logger/lib/src/log_file_manager.dart

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import 'dart:ui';
88
import 'package:flutter/foundation.dart';
99
import 'package:path/path.dart' as p;
1010

11-
class LogFileManager {
12-
LogFileManager._(this._sendPort);
13-
11+
abstract class LogFileManager {
1412
static LogFileManager? _instance;
1513

1614
static LogFileManager? get instance => _instance ??= _fromOtherIsolate();
@@ -20,36 +18,63 @@ class LogFileManager {
2018
static LogFileManager? _fromOtherIsolate() {
2119
final sendPort = IsolateNameServer.lookupPortByName(_logPortName);
2220
if (sendPort == null) {
21+
debugPrint('[mixin_logger] no logger isolate found');
2322
return null;
2423
}
25-
return LogFileManager._(sendPort);
24+
return _LogFileMangerForOtherIsolate(sendPort);
2625
}
2726

2827
static Future<void> init(
2928
String logDir,
3029
int maxFileCount,
31-
int maxFileLength,
32-
) async {
30+
int maxFileLength, {
31+
String? fileLeading,
32+
}) async {
3333
final receiver = ReceivePort();
3434
await Isolate.spawn(
3535
_logIsolate,
36-
[receiver.sendPort, logDir, maxFileCount, maxFileLength],
36+
[
37+
receiver.sendPort,
38+
logDir,
39+
maxFileCount,
40+
maxFileLength,
41+
fileLeading,
42+
],
3743
);
38-
final sendPort = await receiver.first as SendPort;
39-
final removed = IsolateNameServer.removePortNameMapping(_logPortName);
40-
if (removed) {
41-
debugPrint('Removed old logger isolate. this is ok if hot restarted app');
42-
}
43-
IsolateNameServer.registerPortWithName(sendPort, _logPortName);
44-
}
44+
final completer = Completer<void>();
45+
receiver.listen((message) {
46+
if (message is SendPort) {
47+
final sendPort = message;
48+
final removed = IsolateNameServer.removePortNameMapping(_logPortName);
49+
if (removed) {
50+
debugPrint(
51+
'Removed old logger isolate. this is ok if hot restarted app');
52+
}
53+
IsolateNameServer.registerPortWithName(sendPort, _logPortName);
54+
completer.complete();
55+
} else {
56+
assert(false, 'unknown message: $message');
57+
}
58+
});
4559

46-
final SendPort _sendPort;
60+
return completer.future;
61+
}
4762

4863
static Future<void> _logIsolate(List<dynamic> args) async {
4964
final responsePort = args[0] as SendPort;
5065
final messageReceiver = ReceivePort();
5166
final dir = args[1] as String;
52-
final logFileHandler = LogFileHandler(dir);
67+
final maxFileCount = args[2] as int;
68+
final maxFileLength = args[3] as int;
69+
final fileLeading = args[4] as String?;
70+
71+
final logFileHandler = LogFileHandler(
72+
dir,
73+
maxFileCount: maxFileCount,
74+
maxFileLength: maxFileLength,
75+
fileLeading: fileLeading,
76+
);
77+
LogFileManager._instance = _LogFileManagerForLogIsolate(logFileHandler);
5378
messageReceiver.listen((message) {
5479
if (message is String) {
5580
logFileHandler.write(message);
@@ -58,11 +83,32 @@ class LogFileManager {
5883
responsePort.send(messageReceiver.sendPort);
5984
}
6085

86+
Future<void> write(String message);
87+
}
88+
89+
class _LogFileMangerForOtherIsolate implements LogFileManager {
90+
_LogFileMangerForOtherIsolate(this._sendPort);
91+
92+
final SendPort _sendPort;
93+
94+
@override
6195
Future<void> write(String message) async {
6296
_sendPort.send(message);
6397
}
6498
}
6599

100+
class _LogFileManagerForLogIsolate implements LogFileManager {
101+
_LogFileManagerForLogIsolate(this.handler);
102+
103+
final LogFileHandler handler;
104+
105+
@override
106+
Future<void> write(String message) {
107+
handler.write(message);
108+
return Future.value();
109+
}
110+
}
111+
66112
final _fileNameRegex = RegExp(r'^log_\d+.log$');
67113
final _fileNumberExtractRegex = RegExp(r'(?<=_)\d+(?=.log)');
68114

@@ -73,6 +119,7 @@ class LogFileHandler {
73119
this.directory, {
74120
this.maxFileCount = 10,
75121
this.maxFileLength = 1024 * 1024 * 10, // 10 MB
122+
this.fileLeading,
76123
}) : assert(maxFileCount >= 1),
77124
assert(maxFileLength >= 0) {
78125
final dir = Directory(directory);
@@ -108,10 +155,12 @@ class LogFileHandler {
108155

109156
void _prepareOutputFile() {
110157
final File outputFile;
158+
var newFileCreated = false;
111159
if (files.isEmpty) {
112160
final file = File(p.join(directory, _generateFileName(0)));
113161
files[0] = file;
114162
outputFile = file;
163+
newFileCreated = true;
115164
} else {
116165
final max = files.keys.reduce(math.max);
117166
final file = files[max];
@@ -123,6 +172,7 @@ class LogFileHandler {
123172
final file = File(p.join(directory, _generateFileName(nextIndex)));
124173
files[nextIndex] = file;
125174
outputFile = file;
175+
newFileCreated = true;
126176
}
127177
if (files.length > maxFileCount) {
128178
final min = files.keys.reduce(math.min);
@@ -142,6 +192,10 @@ class LogFileHandler {
142192
}
143193
_logFile = outputFile;
144194
_currentFileLength = outputFile.lengthSync();
195+
if (newFileCreated && fileLeading != null) {
196+
write(fileLeading!);
197+
write('\n');
198+
}
145199
}
146200

147201
final String directory;
@@ -155,6 +209,8 @@ class LogFileHandler {
155209

156210
final int maxFileLength;
157211

212+
final String? fileLeading;
213+
158214
void write(String message) {
159215
assert(_logFile != null, 'Log file is null');
160216
final bytes = utf8.encode('$message\n');

packages/mixin_logger/lib/src/write_log_to_file_io.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Future<void> initLogger(
1212
String logDir,
1313
int maxFileCount,
1414
int maxFileLength,
15+
String? fileLeading,
1516
) async {
16-
await LogFileManager.init(logDir, maxFileCount, maxFileLength);
17+
await LogFileManager.init(
18+
logDir,
19+
maxFileCount,
20+
maxFileLength,
21+
fileLeading: fileLeading,
22+
);
1723
}

packages/mixin_logger/lib/src/write_log_to_file_web.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ Future<void> initLogger(
88
String logDir,
99
int maxFileCount,
1010
int maxFileLength,
11+
String? fileLeading,
1112
) async {}

packages/mixin_logger/test/mixin_logger_test.dart

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ void main() {
117117
expect(fileContent, equals('test\n'));
118118
});
119119

120-
test('test write on other isolate', () async {
120+
test('test write in other isolate', () async {
121121
await LogFileManager.init(dir, 10, 1024 * 1024 * 10);
122122
final manger = LogFileManager.instance!;
123123
await manger.write('main');
@@ -136,6 +136,21 @@ void main() {
136136
final fileContent = File(p.join(dir, 'log_0.log')).readAsStringSync();
137137
expect(fileContent, contains('after initLogger\n'));
138138
});
139+
140+
test('write file leading', () async {
141+
await LogFileManager.init(dir, 10, 200, fileLeading: 'file_leading_test');
142+
143+
for (var i = 0; i < 100; i++) {
144+
LogFileManager.instance!.write('test $i');
145+
}
146+
await Future.delayed(const Duration(milliseconds: 1000));
147+
final fileContent = File(p.join(dir, 'log_0.log')).readAsStringSync();
148+
expect(fileContent, startsWith('file_leading_test'));
149+
final fileContent1 = File(p.join(dir, 'log_1.log')).readAsStringSync();
150+
expect(fileContent1, startsWith('file_leading_test'));
151+
final fileContent2 = File(p.join(dir, 'log_2.log')).readAsStringSync();
152+
expect(fileContent2, startsWith('file_leading_test'));
153+
});
139154
}
140155

141156
void _writeLog(String message) {

0 commit comments

Comments
 (0)