Skip to content

Commit a86d60b

Browse files
feat: Implementing log levels (#495)
* initial implementation without tests * fixes a few tests * fixes more tests * adds several tests for log level * adds more tests * optimisation * changes info log to optionally log to the console * small change * fixes tests * changes as per plugin interface * changes version
1 parent f881c94 commit a86d60b

File tree

17 files changed

+702
-53
lines changed

17 files changed

+702
-53
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [unreleased]
99

10+
## [3.16.0] - 2022-08-18
11+
12+
- Changes logging level of API start / finished & Cronjob start / finished to be `INFO` level instead of `DEBUG` level.
13+
- Added new config `log_level` to set logging level. Possible values are `DEBUG` | `INFO` | `WARN` | `ERROR` |
14+
`NONE`. As an example, setting the log level to `WARN` would make the core print out `WARN` and `ERROR` level logs.
15+
1016
## [3.15.1] - 2022-08-10
1117

1218
- Updates UserIdMapping recipe to resolve UserId Mappings for Auth recipes in the core itself
1319

1420
## [3.15.0] - 2022-07-25
1521

1622
- Adds UserIdMapping recipe
17-
- Support for collecting and displaying failing tests
23+
- Support for collecting and displaying failing tests
1824

1925
### Database changes
2026

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
1919
// }
2020
//}
2121

22-
version = "3.15.1"
22+
version = "3.16.0"
2323

2424

2525
repositories {

config.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,7 @@ core_config_version: 0
102102

103103
# (OPTIONAL | Default: 1). Number of concurrent argon2 hashes that can happen at the same time for sign up or sign
104104
# in requests.
105-
# argon2_hashing_pool_size:
105+
# argon2_hashing_pool_size:
106+
107+
# (OPTIONAL | Default: "INFO"). Logging level for the core. Values are "DEBUG" | "INFO" | "WARN" | "ERROR" | "NONE"
108+
# log_level:

devConfig.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,6 @@ disable_telemetry: true
103103
# (OPTIONAL | Default: 1). Number of concurrent argon2 hashes that can happen at the same time for sign up or sign
104104
# in requests.
105105
# argon2_hashing_pool_size:
106+
107+
# (OPTIONAL | Default: "INFO"). Logging level for the core. Values are "DEBUG" | "INFO" | "WARN" | "ERROR" | "NONE"
108+
# log_level:

pluginInterfaceSupported.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"_comment": "contains a list of plugin interfaces branch names that this core supports",
33
"versions": [
4-
"2.17"
4+
"2.18"
55
]
66
}

src/main/java/io/supertokens/Main.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void start(String[] args) {
118118
ProcessState.getInstance(this).addState(ProcessState.PROCESS_STATE.SHUTTING_DOWN, null);
119119
stopApp();
120120

121-
Logging.info(this, "Goodbye");
121+
Logging.info(this, "Goodbye", true);
122122
} catch (Exception e) {
123123

124124
ProcessState.getInstance(this).addState(ProcessState.PROCESS_STATE.SHUTTING_DOWN, null);
@@ -143,14 +143,16 @@ private void init() throws IOException {
143143
// Handle kill signal gracefully
144144
handleKillSignalForWhenItHappens();
145145

146-
// loading storage layer
147-
StorageLayer.init(this, CLIOptions.get(this).getInstallationPath() + "plugin/",
146+
// loading configs for core.
147+
Config.loadConfig(this,
148148
CLIOptions.get(this).getConfigFilePath() == null
149149
? CLIOptions.get(this).getInstallationPath() + "config.yaml"
150150
: CLIOptions.get(this).getConfigFilePath());
151151

152-
// loading configs for core.
153-
Config.loadConfig(this,
152+
Logging.info(this, "Completed config.yaml loading.", true);
153+
154+
// loading storage layer
155+
StorageLayer.init(this, CLIOptions.get(this).getInstallationPath() + "plugin/",
154156
CLIOptions.get(this).getConfigFilePath() == null
155157
? CLIOptions.get(this).getInstallationPath() + "config.yaml"
156158
: CLIOptions.get(this).getConfigFilePath());
@@ -161,8 +163,6 @@ private void init() throws IOException {
161163
// init file logging
162164
Logging.initFileLogging(this);
163165

164-
Logging.info(this, "Completed config.yaml loading.");
165-
166166
// initialise cron job handler
167167
Cronjobs.init(this);
168168

@@ -217,7 +217,7 @@ private void init() throws IOException {
217217
// NOTE: If the message below is changed, make sure to also change the corresponding check in the CLI program
218218
// for start command
219219
Logging.info(this, "Started SuperTokens on " + Config.getConfig(this).getHost(this) + ":"
220-
+ Config.getConfig(this).getPort(this) + " with PID: " + ProcessHandle.current().pid());
220+
+ Config.getConfig(this).getPort(this) + " with PID: " + ProcessHandle.current().pid(), true);
221221
}
222222

223223
@TestOnly
@@ -314,8 +314,24 @@ public void killForTestingAndWaitForShutdown() throws InterruptedException {
314314
// must not throw any error
315315
// must wait for everything to finish and only then exit
316316
private void stopApp() {
317-
Logging.info(this, "Stopping SuperTokens...");
318317
try {
318+
// We do this first because it was initialized first.
319+
// so if something else fails below due to config not initialized,
320+
// then at least this will be cleared.
321+
if (this.shutdownHook != null) {
322+
try {
323+
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
324+
} catch (IllegalStateException e) {
325+
// we are shutting down already.. so doesn't matter
326+
}
327+
}
328+
329+
// Note that logging may throw an error if the config
330+
// was not loaded due to an error in config. But this is OK
331+
// since we load config before loading anything else
332+
// below this, and this whole block is surrounded in a
333+
// try / catch.
334+
Logging.info(this, "Stopping SuperTokens...", true);
319335
Webserver.getInstance(this).stop();
320336
Cronjobs.shutdownAndAwaitTermination(this);
321337
if (!Main.isTesting) {
@@ -340,19 +356,12 @@ private void stopApp() {
340356
*/
341357
}
342358
}
343-
if (this.shutdownHook != null) {
344-
try {
345-
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
346-
} catch (IllegalStateException e) {
347-
// we are shutting down already.. so doesn't matter
348-
}
349-
}
350359
removeDotStartedFileForThisProcess();
351360
Logging.stopLogging(this);
352361
// uncomment this when you want to confirm that processes are actually shut.
353362
// printRunningThreadNames();
354363

355-
} catch (Exception ignored) {
364+
} catch (Throwable ignored) {
356365

357366
}
358367
}

src/main/java/io/supertokens/config/Config.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public static void loadConfig(Main main, String configFilePath) {
5050
return;
5151
}
5252
main.getResourceDistributor().setResource(RESOURCE_KEY, new Config(main, configFilePath));
53+
Logging.info(main, "Loading supertokens config.", true);
5354
}
5455

5556
public static CoreConfig getConfig(Main main) {
@@ -60,7 +61,6 @@ public static CoreConfig getConfig(Main main) {
6061
}
6162

6263
private CoreConfig loadCoreConfig(String configFilePath) throws IOException {
63-
Logging.info(main, "Loading supertokens config.");
6464
final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
6565
CoreConfig config = mapper.readValue(new File(configFilePath), CoreConfig.class);
6666
config.validateAndInitialise(main);

src/main/java/io/supertokens/config/CoreConfig.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@
2121
import io.supertokens.Main;
2222
import io.supertokens.cliOptions.CLIOptions;
2323
import io.supertokens.exceptions.QuitProgramException;
24+
import io.supertokens.pluginInterface.LOG_LEVEL;
2425
import org.jetbrains.annotations.TestOnly;
2526

2627
import java.io.File;
2728
import java.io.IOException;
29+
import java.util.HashSet;
30+
import java.util.Set;
2831

2932
@JsonIgnoreProperties(ignoreUnknown = true)
3033
public class CoreConfig {
@@ -110,6 +113,36 @@ public class CoreConfig {
110113
@JsonProperty
111114
private String base_path = "";
112115

116+
@JsonProperty
117+
private String log_level = "INFO";
118+
119+
private Set<LOG_LEVEL> allowedLogLevels = null;
120+
121+
public Set<LOG_LEVEL> getLogLevels(Main main) {
122+
if (allowedLogLevels != null) {
123+
return allowedLogLevels;
124+
}
125+
LOG_LEVEL logLevel = LOG_LEVEL.valueOf(this.log_level.toUpperCase());
126+
allowedLogLevels = new HashSet<>();
127+
if (logLevel == LOG_LEVEL.NONE) {
128+
return allowedLogLevels;
129+
}
130+
allowedLogLevels.add(LOG_LEVEL.ERROR);
131+
if (logLevel == LOG_LEVEL.ERROR) {
132+
return allowedLogLevels;
133+
}
134+
allowedLogLevels.add(LOG_LEVEL.WARN);
135+
if (logLevel == LOG_LEVEL.WARN) {
136+
return allowedLogLevels;
137+
}
138+
allowedLogLevels.add(LOG_LEVEL.INFO);
139+
if (logLevel == LOG_LEVEL.INFO) {
140+
return allowedLogLevels;
141+
}
142+
allowedLogLevels.add(LOG_LEVEL.DEBUG);
143+
return allowedLogLevels;
144+
}
145+
113146
public String getBasePath() {
114147
String base_path = this.base_path; // Don't modify the original value from the config
115148
if (base_path == null || base_path.equals("/") || base_path.isEmpty()) {
@@ -375,6 +408,13 @@ void validateAndInitialise(Main main) throws IOException {
375408
}
376409
}
377410

411+
if (!log_level.equalsIgnoreCase("info") && !log_level.equalsIgnoreCase("none")
412+
&& !log_level.equalsIgnoreCase("error") && !log_level.equalsIgnoreCase("warn")
413+
&& !log_level.equalsIgnoreCase("debug")) {
414+
throw new QuitProgramException(
415+
"'log_level' config must be one of \"NONE\",\"DEBUG\", \"INFO\", \"WARN\" or \"ERROR\".");
416+
}
417+
378418
if (!getInfoLogPath(main).equals("null")) {
379419
File infoLog = new File(getInfoLogPath(main));
380420
if (!infoLog.exists()) {

src/main/java/io/supertokens/cronjobs/CronTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,17 @@ public abstract class CronTask extends ResourceDistributor.SingletonResource imp
3030
protected CronTask(String jobName, Main main) {
3131
this.jobName = jobName;
3232
this.main = main;
33-
Logging.info(main, "Starting task: " + jobName);
33+
Logging.info(main, "Starting task: " + jobName, false);
3434
}
3535

3636
void shutdownIsGoingToBeCalled() {
37-
Logging.info(main, "Stopping task: " + jobName);
37+
Logging.info(main, "Stopping task: " + jobName, false);
3838
}
3939

4040
@Override
4141
public void run() {
4242
try {
43-
Logging.debug(main, "Cronjob started: " + jobName);
43+
Logging.info(main, "Cronjob started: " + jobName, false);
4444
doTask();
4545
} catch (Exception e) {
4646
ProcessState.getInstance(main).addState(ProcessState.PROCESS_STATE.CRON_TASK_ERROR_LOGGING, e);
@@ -49,7 +49,7 @@ public void run() {
4949
main.wakeUpMainThreadToShutdown();
5050
}
5151
}
52-
Logging.debug(main, "Cronjob finished: " + jobName);
52+
Logging.info(main, "Cronjob finished: " + jobName, false);
5353
}
5454

5555
protected abstract void doTask() throws Exception;

src/main/java/io/supertokens/inmemorydb/Start.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@
2020
import io.supertokens.Main;
2121
import io.supertokens.ProcessState;
2222
import io.supertokens.ResourceDistributor;
23-
import io.supertokens.emailpassword.EmailPassword;
2423
import io.supertokens.emailverification.EmailVerification;
2524
import io.supertokens.emailverification.exception.EmailAlreadyVerifiedException;
2625
import io.supertokens.inmemorydb.config.Config;
2726
import io.supertokens.inmemorydb.queries.*;
2827
import io.supertokens.pluginInterface.KeyValueInfo;
28+
import io.supertokens.pluginInterface.LOG_LEVEL;
2929
import io.supertokens.pluginInterface.RECIPE_ID;
3030
import io.supertokens.pluginInterface.STORAGE_TYPE;
31-
import io.supertokens.pluginInterface.authRecipe.AuthRecipeStorage;
3231
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
3332
import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo;
3433
import io.supertokens.pluginInterface.emailpassword.UserInfo;
@@ -48,7 +47,6 @@
4847
import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo;
4948
import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException;
5049
import io.supertokens.pluginInterface.jwt.sqlstorage.JWTRecipeSQLStorage;
51-
import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage;
5250
import io.supertokens.pluginInterface.passwordless.PasswordlessCode;
5351
import io.supertokens.pluginInterface.passwordless.PasswordlessDevice;
5452
import io.supertokens.pluginInterface.passwordless.exception.*;
@@ -83,9 +81,9 @@
8381
import java.sql.SQLException;
8482
import java.sql.SQLTransactionRollbackException;
8583
import java.util.ArrayList;
86-
import java.util.Arrays;
8784
import java.util.HashMap;
8885
import java.util.List;
86+
import java.util.Set;
8987

9088
public class Start
9189
implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage,
@@ -126,7 +124,7 @@ public STORAGE_TYPE getType() {
126124
}
127125

128126
@Override
129-
public void loadConfig(String ignored) {
127+
public void loadConfig(String ignored, Set<LOG_LEVEL> logLevel) {
130128
Config.loadConfig(this);
131129
}
132130

0 commit comments

Comments
 (0)