Browse Source

readme and refactor

master
Tobias Hübner 5 months ago
parent
commit
15811bcf44
8 changed files with 84 additions and 34 deletions
  1. +22
    -5
      README.md
  2. +2
    -2
      src/main/logging/DeleteFileLogs.java
  3. +1
    -1
      src/main/logging/LogLevel.java
  4. +31
    -16
      src/main/logging/LogPoint.java
  5. +15
    -2
      src/main/logging/Logger.java
  6. +5
    -2
      src/main/logging/loggers/FileLogger.java
  7. +6
    -3
      src/main/logging/loggers/GelfLogger.java
  8. +2
    -3
      src/test/logging/T_Logger.java

+ 22
- 5
README.md View File

@@ -1,8 +1,25 @@
# Logging

Features:
Thread Safe fast access logger. When using most log frameworks it always bothered me that you had to initialize multiple loggers and even pass them down everywhere.

* logging exceptions with full stacktraces
* Uncaught exception logging (any exception that dont get caught in a try catch)
* Automatic deletion of old logs
* Thread safe logging
This is an attempt to reduce the boiler plate code using one static abstract class, that can be initialized with any logger that implements a predefined interface.

# Usage

```java


public static void main(String[] args){
// initialize a Logger
FileLogger fl = new FileLogger("/path/to/logs");
LogPoint.initialize(fl);
LogPoint.log(LogLevel.Trace, "your message");
LogPoint.log(new Exception("an exception has occured"));
}

```

After initialization logs can be send anywhere using the static method.

The Logger is also written in a way to be compatible, meaning you don't have to initialize it. If you log messages to it, without initializing it, it will simply print them to System.out.

src/main/logging/Delete.java → src/main/logging/DeleteFileLogs.java View File

@@ -6,7 +6,7 @@ import java.nio.file.Paths;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

public class Delete {
public class DeleteFileLogs {

public static int DELETE_LOGS_OLDER_THAN_DAYS = 30;

@@ -16,7 +16,7 @@ public class Delete {
* @param log_path directory
* @throws IOException
*/
public static void delete_old_logs(String log_path) throws IOException {
public static void delete_old(String log_path) throws IOException {
Files.find(Paths.get(log_path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> {

src/main/logging/Log_level.java → src/main/logging/LogLevel.java View File

@@ -1,5 +1,5 @@
package logging;

public enum Log_level {
public enum LogLevel {
Error, Warning, Trace, Uncaught
}

+ 31
- 16
src/main/logging/LogPoint.java View File

@@ -1,25 +1,22 @@
package logging;

import java.util.HashSet;
import java.util.Set;

import static logging.Logger.stack_to_string;

public class LogPoint {

/**
* Static class that holds a reference to a {@link Logger} and uses this
* logger to write all logs
*/
public abstract class LogPoint {

// Log all uncaught exceptions as uncaught
static {
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
log(e, Log_level.Uncaught);
log(e, LogLevel.Uncaught);
});
}
/**
* Needs to be volatile because:
* 1. Can change at any point
* 2. Log methods that depend on it could be called from any thread.
*/
private static volatile Logger logger ;

private static Logger logger ;

public static void initialize(Logger logger){
if(LogPoint.logger != null){
@@ -28,11 +25,20 @@ public class LogPoint {
LogPoint.logger = logger;
}

public static void log(String... logtext) {
log(Log_level.Trace, logtext);
/**
* Log one or multiple log messages
* @param logtexts message(s)
*/
public static void log(String... logtexts) {
log(LogLevel.Trace, logtexts);
}

public static void log(Log_level ll, String... logtext) {
/**
* Log one or multiple logs messages with a specified severity level
* @param ll severity level
* @param logtext message(s)
*/
public static void log(LogLevel ll, String... logtext) {
if(logger != null){
String format = logger.format(ll, logtext);
System.out.println(format);
@@ -42,11 +48,20 @@ public class LogPoint {
}
}

/**
* Log an exception with the severity ERROR
* @param e the exception
*/
public static void log(Throwable e) {
log(e, Log_level.Error);
log(e, LogLevel.Error);
}

public static void log(Throwable e, Log_level ll) {
/**
* Log an exception with a specified severity
* @param e the exeption
* @param ll severity level
*/
public static void log(Throwable e, LogLevel ll) {
log(ll, stack_to_string(e));
}
}

+ 15
- 2
src/main/logging/Logger.java View File

@@ -5,20 +5,30 @@ import java.io.StringWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
* Implement this interface to set a Logger at {@link LogPoint#initialize(Logger)}
*/
public interface Logger {

void log(String format);

String format(Log_level ll, String... messsages);
String format(LogLevel ll, String... messsages);

/**
* Helper function
* @param e exception
* @return full exception string
*/
static String stack_to_string(Throwable e) {
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
return errors.toString();
}

/**
* @return hostname
*/
static String get_station(){
// determine station and user
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
@@ -27,6 +37,9 @@ public interface Logger {
return "unknown";
}

/**
* @return username
*/
static String get_user(){
return System.getProperty("user.name") != null ? System.getProperty("user.name") : "unknown";
}


+ 5
- 2
src/main/logging/loggers/FileLogger.java View File

@@ -1,6 +1,6 @@
package logging.loggers;

import logging.Log_level;
import logging.LogLevel;
import logging.Logger;

import java.io.BufferedWriter;
@@ -15,6 +15,9 @@ import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;

/**
* Logs to file with a worker queue
*/
public class FileLogger implements Logger {

private final String log_path;
@@ -79,7 +82,7 @@ public class FileLogger implements Logger {
}

@Override
public String format(Log_level ll, String... text) {
public String format(LogLevel ll, String... text) {
return String.format(
"""
Level : %s\r


+ 6
- 3
src/main/logging/loggers/GelfLogger.java View File

@@ -1,11 +1,14 @@
package logging.loggers;

import logging.Log_level;
import logging.LogLevel;
import logging.Logger;

import java.io.IOException;
import java.net.*;

/**
* Writes to UDP Gelf Input like Graylog
*/
public class GelfLogger implements Logger {

private final DatagramSocket socket;
@@ -23,7 +26,7 @@ public class GelfLogger implements Logger {
}

@Override
public String format(Log_level ll, String... messsages) {
public String format(LogLevel ll, String... messages) {
return String.format("""
{
"version": "1.1",
@@ -35,7 +38,7 @@ public class GelfLogger implements Logger {
"program_name" : "%s"
}
""", Logger.get_station(), String.join("\n", messsages), Logger.get_user(),
""", Logger.get_station(), String.join("\n", messages), Logger.get_user(),
Thread.currentThread().getName() + ":" + Thread.currentThread().getId(),
ll.toString(), program_name);
}


+ 2
- 3
src/test/logging/T_Logger.java View File

@@ -5,7 +5,6 @@ import logging.loggers.GelfLogger;
import org.junit.jupiter.api.*;

import java.io.IOException;
import java.net.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
@@ -21,7 +20,7 @@ public class T_Logger {
FileLogger fl = new FileLogger("logs");
String message = "heyhohoho";

fl.log(fl.format(Log_level.Error, message));
fl.log(fl.format(LogLevel.Error, message));
Thread.sleep(2000);

String logfile = Files.readString(Paths.get("logs", fl.get_logfile_name()));
@@ -37,7 +36,7 @@ public class T_Logger {
Set<Thread> threads = new HashSet<>();

for(int i = 0; i < 100; i++){
threads.add(new Thread(()->gl.log(gl.format(Log_level.Error, Logger.stack_to_string(new Exception("hey"))))));
threads.add(new Thread(()->gl.log(gl.format(LogLevel.Error, Logger.stack_to_string(new Exception("hey"))))));
}

threads.forEach(t -> t.start());


Loading…
Cancel
Save