cordova-plugin-unvired-logger 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -200,12 +200,12 @@ testLogger();
200
200
  ## Platform-Specific Details
201
201
 
202
202
  ### Android
203
- - Logs are stored in the app's internal storage: `/data/data/[package]/files/[userId]/AppLogs/`
203
+ - Logs are stored in the app's internal storage: `/data/data/[package]/files/[userId]/`
204
204
  - Requires storage permissions for external storage access
205
205
  - Automatic log rotation when file size exceeds 5MB
206
206
 
207
207
  ### iOS
208
- - Logs are stored in the app's Documents directory: `Documents/[userId]/AppLogs/`
208
+ - Logs are stored in the app's Documents directory: `Documents/[userId]/`
209
209
  - Uses native Swift implementation for optimal performance
210
210
  - Automatic log rotation when file size exceeds 5MB
211
211
 
@@ -230,14 +230,13 @@ testLogger();
230
230
 
231
231
  Logs are stored in the following structure:
232
232
  ```
233
- {userDataPath}/{userId}/AppLogs/log.txt
234
- {userDataPath}/{userId}/AppLogs/log_backup.txt
233
+ {userDataPath}/{userId}/log.txt
234
+ {userDataPath}/{userId}/log_backup.txt
235
235
  ```
236
236
 
237
237
  Where:
238
238
  - `userDataPath` is the Electron app's user data directory
239
- - `userId` is the user identifier you provide
240
- - `AppLogs` is the log folder name
239
+ - `userId` is the log folder name
241
240
  - `log.txt` is the current log file
242
241
  - `log_backup.txt` is the backup file (created during rotation)
243
242
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-unvired-logger",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A logger plugin for Electron, Android, Browser, and iOS that appends logs to log.txt files organized by user ID.",
5
5
  "cordova": {
6
6
  "id": "cordova-plugin-unvired-logger",
package/plugin.xml CHANGED
@@ -1,5 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <plugin id="cordova-plugin-unvired-logger" version="0.0.10"
2
+ <plugin id="cordova-plugin-unvired-logger" version="0.0.12"
3
3
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
4
4
  xmlns:android="http://schemas.android.com/apk/res/android">
5
5
 
@@ -16,12 +16,11 @@ import java.util.Locale;
16
16
 
17
17
  public class Logger extends CordovaPlugin {
18
18
 
19
- private static final String LOG_FOLDER_NAME = "AppLogs";
20
19
  private static final String LOG_FILE_NAME = "log.txt";
21
20
  private static final String BACKUP_LOG_FILE_NAME = "log_backup.txt";
22
21
  private static final long MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
23
22
 
24
- private String defaultLogLevel = "info";
23
+ private String defaultLogLevel = "important";
25
24
 
26
25
  @Override
27
26
  public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
@@ -111,8 +110,8 @@ public class Logger extends CordovaPlugin {
111
110
  private boolean shouldSkipLog(String level) {
112
111
  String normLevel = level.toLowerCase();
113
112
  String normDefault = defaultLogLevel.toLowerCase();
114
- if (normDefault.equals("error") && (normLevel.equals("debug") || normLevel.equals("info"))) return true;
115
- if (normDefault.equals("info") && normLevel.equals("debug")) return true;
113
+ if (normDefault.equals("error") && (normLevel.equals("debug") || normLevel.equals("important"))) return true;
114
+ if (normDefault.equals("important") && normLevel.equals("debug")) return true;
116
115
  return false;
117
116
  }
118
117
 
@@ -203,7 +202,7 @@ public class Logger extends CordovaPlugin {
203
202
 
204
203
  private File getUserLogDir(String userId) {
205
204
  Context ctx = cordova.getActivity().getApplicationContext();
206
- File userDir = new File(ctx.getFilesDir(), userId + File.separator + LOG_FOLDER_NAME);
205
+ File userDir = new File(ctx.getFilesDir(), userId);
207
206
  if (!userDir.exists()) userDir.mkdirs();
208
207
  return userDir;
209
208
  }
@@ -229,7 +228,7 @@ public class Logger extends CordovaPlugin {
229
228
 
230
229
  private String getStringFromLevel(String level) {
231
230
  switch (level.toLowerCase()) {
232
- case "info": return "IMPORTANT";
231
+ case "important": return "IMPORTANT";
233
232
  case "error": return "ERROR";
234
233
  case "debug": return "DEBUG";
235
234
  default: return "";
@@ -1,4 +1,3 @@
1
- const LOG_FOLDER_NAME = 'AppLogs';
2
1
  const LOG_FILE_NAME = 'log.txt';
3
2
  const BACKUP_LOG_FILE_NAME = 'log_backup.txt';
4
3
  const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
@@ -6,7 +5,7 @@ const MAX_LOG_SIZE = 5 * 1024 * 1024; // 5MB
6
5
  const LogLevel = {
7
6
  Debug: 'debug',
8
7
  Error: 'error',
9
- Info: 'info',
8
+ Info: 'important',
10
9
  };
11
10
 
12
11
  let defaultLogLevel = LogLevel.Info;
@@ -29,12 +28,12 @@ module.exports.logInfo = async function (successCallback, errorCallback, args) {
29
28
 
30
29
  module.exports.setLogLevel = function(successCallback, errorCallback, level) {
31
30
  // If level is an array (from Cordova), extract the first element
32
- if (Array.isArray(level)) {
33
- defaultLogLevel = level[0];
34
- } else {
35
- defaultLogLevel = level;
36
- }
37
- successCallback("")
31
+ let levelToSet = Array.isArray(level) ? level[0] : level;
32
+
33
+ // Normalize the level to lowercase for consistency
34
+ defaultLogLevel = (levelToSet || '').toLowerCase();
35
+
36
+ successCallback("");
38
37
  }
39
38
 
40
39
  module.exports.getLogLevel = function(successCallback, errorCallback) {
@@ -3,14 +3,42 @@ const path = require('path');
3
3
  const fs = require('fs');
4
4
  const fsPromises = fs.promises;
5
5
 
6
- const LOG_FOLDER_NAME = 'log';
6
+ // --- Proper async lock for log file operations ---
7
+ let logLockPromise = Promise.resolve();
8
+ let isLocked = false;
9
+
10
+ async function withLogLock(fn) {
11
+ // Wait for any existing lock to complete
12
+ await logLockPromise;
13
+
14
+ // Create a new promise for this operation
15
+ const operationPromise = (async () => {
16
+ try {
17
+ isLocked = true;
18
+ const result = await fn();
19
+ return result;
20
+ } catch (error) {
21
+ console.error('[Logger] Error in log operation:', error);
22
+ // Return error message instead of throwing to prevent unhandled promise rejections
23
+ return `Error in log operation: ${error.message}`;
24
+ } finally {
25
+ isLocked = false;
26
+ }
27
+ })();
28
+
29
+ // Update the lock promise to wait for this operation
30
+ logLockPromise = operationPromise;
31
+
32
+ return operationPromise;
33
+ }
34
+
7
35
  const LOG_FILE_NAME = 'log.txt';
8
36
  const BACKUP_LOG_FILE_NAME = 'log_backup.txt';
9
37
 
10
38
  const LogLevel = {
11
39
  Debug: 'debug',
12
40
  Error: 'error',
13
- Info: 'info',
41
+ Info: 'important',
14
42
  };
15
43
 
16
44
  let defaultLogLevel = LogLevel.Info;
@@ -29,15 +57,11 @@ function logInfo(args) {
29
57
  }
30
58
 
31
59
  function setLogLevel(level) {
32
- return new Promise((resolve) => {
33
- // If level is an array (from Cordova), extract the first element
34
- if (Array.isArray(level)) {
35
- defaultLogLevel = level[0];
36
- } else {
37
- defaultLogLevel = level;
38
- }
39
- resolve();
40
- });
60
+ // If level is an array (from Cordova), extract the first element
61
+ let levelToSet = Array.isArray(level) ? level[0] : level;
62
+
63
+ // Normalize the level to lowercase for consistency
64
+ defaultLogLevel = (levelToSet || '').toLowerCase();
41
65
  }
42
66
 
43
67
  function getLogLevel() {
@@ -68,14 +92,10 @@ function getLogFileContent(args) {
68
92
  }
69
93
 
70
94
  function clearLogFile(args) {
71
- return new Promise(async (resolve, reject) => {
72
- try {
73
- const logPath = getLogFileURL(args);
74
- await fsPromises.writeFile(logPath, '');
75
- resolve();
76
- } catch (err) {
77
- reject(new Error(`Failed to clear log file: ${err.message}`));
78
- }
95
+ return withLogLock(async () => {
96
+ const logPath = getLogFileURL(args);
97
+ await fsPromises.writeFile(logPath, '');
98
+ return 'Log file cleared successfully';
79
99
  });
80
100
  }
81
101
 
@@ -101,69 +121,58 @@ function getBackupLogFileContent(args) {
101
121
  }
102
122
 
103
123
  function copyLogToBackup(args) {
104
- return new Promise(async (resolve, reject) => {
105
- try {
106
- const logPath = getLogFileURL(args);
107
- const backupPath = getBackupLogFileURL(args);
108
- await fsPromises.copyFile(logPath, backupPath);
109
- resolve();
110
- } catch (err) {
111
- reject(new Error(`Failed to copy log file to backup: ${err.message}`));
112
- }
124
+ return withLogLock(async () => {
125
+ const logPath = getLogFileURL(args);
126
+ const backupPath = getBackupLogFileURL(args);
127
+ await fsPromises.copyFile(logPath, backupPath);
128
+ return 'Log file copied to backup successfully';
113
129
  });
114
130
  }
115
131
 
116
132
  function loggerWithLevel(args) {
117
- return new Promise(async (resolve, reject) => {
118
- try {
119
-
120
- if (!args || !args[0]) {
121
- return reject(new Error("Invalid arguments provided"));
122
- }
123
- const { userId, level, sourceClass, sourceMethod, message } = args[0];
124
- if (!userId) return reject(new Error("userId is required"));
125
- if (!level) return reject(new Error("level is required"));
126
- if (!message) return reject(new Error("message is required"));
127
-
128
- // Normalize log levels for comparison
129
- const normalizedDefaultLogLevel = (defaultLogLevel || '').toLowerCase();
130
- const normalizedLevel = (level || '').toLowerCase();
131
-
132
- // Log level filtering using normalized values
133
- if (
134
- (normalizedDefaultLogLevel === LogLevel.Error && (normalizedLevel === LogLevel.Debug || normalizedLevel === LogLevel.Info)) ||
135
- (normalizedDefaultLogLevel === LogLevel.Info && normalizedLevel === LogLevel.Debug)
136
- ) {
137
- return resolve();
138
- }
139
-
140
- await checkAndRotateLogFile(args);
133
+ return withLogLock(async () => {
134
+ if (!args || !args[0]) throw new Error("Invalid arguments provided");
135
+ const { userId, level, sourceClass, sourceMethod, message } = args[0];
136
+ if (!userId) throw new Error("userId is required");
137
+ if (!level) throw new Error("level is required");
138
+ if (!message) throw new Error("message is required");
139
+
140
+ // Normalize log levels for comparison
141
+ const normalizedDefaultLogLevel = (defaultLogLevel || '').toLowerCase();
142
+ const normalizedLevel = (level || '').toLowerCase();
143
+
144
+ // Log level filtering using normalized values
145
+ if (
146
+ (normalizedDefaultLogLevel === LogLevel.Error && (normalizedLevel === LogLevel.Debug || normalizedLevel === LogLevel.Info)) ||
147
+ (normalizedDefaultLogLevel === LogLevel.Info && normalizedLevel === LogLevel.Debug)
148
+ ) {
149
+ return;
150
+ }
141
151
 
142
- const logPath = getLogFileURL(args);
143
- const currentDate = new Date();
144
- const formatter = new Intl.DateTimeFormat('en-US', {
145
- day: '2-digit', month: '2-digit', year: 'numeric',
146
- hour: '2-digit', minute: '2-digit', second: '2-digit'
147
- });
148
- const localDateString = formatter.format(currentDate);
149
-
150
- const dateUtc = new Date(currentDate.toUTCString());
151
- const utcFormatter = new Intl.DateTimeFormat('en-US', {
152
- day: '2-digit', month: '2-digit', year: 'numeric',
153
- hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC'
154
- });
155
- const utcDateString = utcFormatter.format(dateUtc);
156
-
157
- let data = `${localDateString} | UTC:${utcDateString} | ${getStringFromLevel(level)} | ${sourceClass} | ${sourceMethod} | ${message}\n`;
158
- if (level === LogLevel.Error) {
159
- data = `⭕️⭕️⭕️ ${data}`;
160
- }
161
- console.log(data);
162
- await fsPromises.appendFile(logPath, data, 'utf8');
163
- resolve(`Logged to ${logPath}`);
164
- } catch (err) {
165
- reject(new Error(`Logging failed: ${err.message}`));
152
+ await checkAndRotateLogFile(args);
153
+
154
+ const logPath = getLogFileURL(args);
155
+ const currentDate = new Date();
156
+ const formatter = new Intl.DateTimeFormat('en-US', {
157
+ day: '2-digit', month: '2-digit', year: 'numeric',
158
+ hour: '2-digit', minute: '2-digit', second: '2-digit'
159
+ });
160
+ const localDateString = formatter.format(currentDate);
161
+
162
+ const dateUtc = new Date(currentDate.toUTCString());
163
+ const utcFormatter = new Intl.DateTimeFormat('en-US', {
164
+ day: '2-digit', month: '2-digit', year: 'numeric',
165
+ hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'UTC'
166
+ });
167
+ const utcDateString = utcFormatter.format(dateUtc);
168
+
169
+ let data = `${localDateString} | UTC:${utcDateString} | ${getStringFromLevel(level)} | ${sourceClass} | ${sourceMethod} | ${message}\n`;
170
+ if (level === LogLevel.Error) {
171
+ data = `⭕️⭕️⭕️ ${data}`;
166
172
  }
173
+ console.log(data);
174
+ await fsPromises.appendFile(logPath, data, 'utf8');
175
+ return `Logged to ${logPath}`;
167
176
  });
168
177
  }
169
178
 
@@ -173,7 +182,7 @@ function getLogDirectory(args) {
173
182
  }
174
183
  const userId = args[0].userId;
175
184
  const userDataPath = app.getPath('userData');
176
- const userPath = path.join(userDataPath, userId, LOG_FOLDER_NAME);
185
+ const userPath = path.join(userDataPath, userId);
177
186
  if (!fs.existsSync(userPath)) {
178
187
  fs.mkdirSync(userPath, { recursive: true });
179
188
  }
@@ -190,21 +199,18 @@ function getStringFromLevel(level) {
190
199
  }
191
200
 
192
201
  function checkAndRotateLogFile(args) {
193
- return new Promise(async (resolve, reject) => {
194
- try {
195
- const logPath = getLogFileURL(args);
196
- if (fs.existsSync(logPath)) {
197
- const stats = await fsPromises.stat(logPath);
198
- if (stats.size > MAX_LOG_SIZE) {
199
- const backupPath = getBackupLogFileURL(args);
200
- await fsPromises.copyFile(logPath, backupPath);
201
- await fsPromises.writeFile(logPath, '');
202
- }
202
+ return withLogLock(async () => {
203
+ const logPath = getLogFileURL(args);
204
+ if (fs.existsSync(logPath)) {
205
+ const stats = await fsPromises.stat(logPath);
206
+ if (stats.size > MAX_LOG_SIZE) {
207
+ const backupPath = getBackupLogFileURL(args);
208
+ await fsPromises.copyFile(logPath, backupPath);
209
+ await fsPromises.writeFile(logPath, '');
210
+ return 'Log file rotated due to size limit';
203
211
  }
204
- resolve();
205
- } catch (err) {
206
- reject(new Error(`Failed to rotate log file: ${err.message}`));
207
212
  }
213
+ return 'Log file rotation check completed';
208
214
  });
209
215
  }
210
216
 
@@ -3,12 +3,11 @@ import UIKit
3
3
 
4
4
  @objc(Logger) class Logger : CDVPlugin {
5
5
 
6
- private let LOG_FOLDER_NAME = "AppLogs"
7
6
  private let LOG_FILE_NAME = "log.txt"
8
7
  private let BACKUP_LOG_FILE_NAME = "log_backup.txt"
9
8
  private let MAX_LOG_SIZE: Int64 = 5 * 1024 * 1024 // 5MB
10
9
 
11
- private var defaultLogLevel: String = "info"
10
+ private var defaultLogLevel: String = "important"
12
11
 
13
12
  @objc(logDebug:)
14
13
  func logDebug(_ command: CDVInvokedUrlCommand) {
@@ -193,10 +192,10 @@ import UIKit
193
192
  let normLevel = level.lowercased()
194
193
  let normDefault = defaultLogLevel.lowercased()
195
194
 
196
- if normDefault == "error" && (normLevel == "debug" || normLevel == "info") {
195
+ if normDefault == "error" && (normLevel == "debug" || normLevel == "important") {
197
196
  return true
198
197
  }
199
- if normDefault == "info" && normLevel == "debug" {
198
+ if normDefault == "important" && normLevel == "debug" {
200
199
  return true
201
200
  }
202
201
  return false
@@ -214,7 +213,7 @@ import UIKit
214
213
 
215
214
  private func getUserLogDir(userId: String) throws -> URL {
216
215
  let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
217
- let userDir = documentsPath.appendingPathComponent(userId).appendingPathComponent(LOG_FOLDER_NAME)
216
+ let userDir = documentsPath.appendingPathComponent(userId)
218
217
 
219
218
  if !FileManager.default.fileExists(atPath: userDir.path) {
220
219
  try FileManager.default.createDirectory(at: userDir, withIntermediateDirectories: true, attributes: nil)
@@ -251,7 +250,7 @@ import UIKit
251
250
 
252
251
  private func getStringFromLevel(level: String) -> String {
253
252
  switch level.lowercased() {
254
- case "info": return "IMPORTANT"
253
+ case "important": return "IMPORTANT"
255
254
  case "error": return "ERROR"
256
255
  case "debug": return "DEBUG"
257
256
  default: return ""
@@ -259,6 +258,11 @@ import UIKit
259
258
  }
260
259
 
261
260
  private func appendToFile(file: URL, data: String) throws {
261
+ let fileManager = FileManager.default
262
+ if !fileManager.fileExists(atPath: file.path) {
263
+ // Create the file if it doesn't exist
264
+ fileManager.createFile(atPath: file.path, contents: nil, attributes: nil)
265
+ }
262
266
  let fileHandle = try FileHandle(forWritingTo: file)
263
267
  fileHandle.seekToEndOfFile()
264
268
  fileHandle.write(data.data(using: .utf8)!)
package/www/logger.js CHANGED
@@ -4,7 +4,7 @@ var exec = require('cordova/exec');
4
4
  exports.LogLevel = {
5
5
  Debug: 'debug',
6
6
  Error: 'error',
7
- Info: 'info'
7
+ Info: 'important'
8
8
  };
9
9
 
10
10
  /**