package org.simantics.scl.compiler.elaboration.java; import org.cojen.classfile.TypeDesc; import org.osgi.service.component.annotations.Component; import org.simantics.scl.compiler.commands.CommandSession; import org.simantics.scl.compiler.common.names.Name; import org.simantics.scl.compiler.compilation.CompilationContext; import org.simantics.scl.compiler.constants.JavaMethod; import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant; import org.simantics.scl.compiler.elaboration.expressions.ELiteral; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.macros.MacroRule; import org.simantics.scl.compiler.elaboration.modules.SCLValue; import org.simantics.scl.compiler.internal.codegen.types.StandardTypeConstructor; import org.simantics.scl.compiler.module.ConcreteModule; import org.simantics.scl.compiler.types.TCon; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; import org.simantics.scl.compiler.types.kinds.Kinds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; @Component public class LoggingModule extends ConcreteModule { private static final String[] LOGGING_METHODS = new String[] { "trace", "debug", "info", "warn", "error" }; public static final TCon Throwable = Types.con("Prelude", "Throwable"); public LoggingModule() { super("LoggingJava"); // Logger TCon Logger = Types.con(getName(), "Logger"); StandardTypeConstructor loggerConstructor = new StandardTypeConstructor(Logger, Kinds.STAR, TypeDesc.forClass(Logger.class)); loggerConstructor.external = true; addTypeDescriptor("Logger", loggerConstructor); // Marker TCon Marker = Types.con(getName(), "Marker"); StandardTypeConstructor markerConstructor = new StandardTypeConstructor(Marker, Kinds.STAR, TypeDesc.forClass(Marker.class)); markerConstructor.external = true; addTypeDescriptor("Marker", markerConstructor); // Common types Type isEnabledType = Types.functionE(Types.PUNIT, Types.PROC, Types.BOOLEAN); Type loggingType = Types.functionE(Types.STRING, Types.PROC, Types.UNIT); Type loggingTypeWithMarker = Types.functionE(new Type[] { Marker, Types.STRING }, Types.PROC, Types.UNIT); Type loggingTypeWithException = Types.functionE(new Type[] { Types.STRING, Throwable }, Types.PROC, Types.UNIT); Type loggingTypeWithMarkerAndException = Types.functionE(new Type[] { Marker, Types.STRING, Throwable }, Types.PROC, Types.UNIT); // Add logging methods for(String methodName : LOGGING_METHODS) { { // isEnabled :: Boolean String completeMethodName = generateIsEnabledName(methodName); JavaMethod javaMethod = new JavaMethod(false, "org/slf4j/Logger", completeMethodName, Types.PROC, Types.BOOLEAN, Logger); SCLValue value = new SCLValue(Name.create(getName(), completeMethodName)); value.setType(isEnabledType); value.setMacroRule(new MacroRule() { @Override public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) { String identifier = resolveModuleIdentifier(context.getCompilationContext()); apply.set(new ELiteral(javaMethod), new Expression[] { new EExternalConstant(LoggerFactory.getLogger(identifier), Logger) }); return apply; } }); addValue(value); } { // logging function with single String-parameter :: String -> () JavaMethod javaMethod = new JavaMethod(false, "org/slf4j/Logger", methodName, Types.PROC, Types.UNIT, Logger, Types.STRING); SCLValue value = new SCLValue(Name.create(getName(), methodName)); value.setType(loggingType); value.setMacroRule(new MacroRule() { @Override public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) { String identifier = resolveModuleIdentifier(context.getCompilationContext()); apply.set(new ELiteral(javaMethod), new Expression[] { new EExternalConstant(LoggerFactory.getLogger(identifier), Logger), apply.parameters[0] }); return apply; } }); addValue(value); } { // logging function with two parameters :: String -> Throwable -> () JavaMethod javaMethod = new JavaMethod(false, "org/slf4j/Logger", methodName, Types.PROC, Types.UNIT, Logger, Types.STRING, Throwable); SCLValue value = new SCLValue(Name.create(getName(), methodName + "E")); value.setType(loggingTypeWithException); value.setMacroRule(new MacroRule() { @Override public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) { String identifier = resolveModuleIdentifier(context.getCompilationContext()); apply.set(new ELiteral(javaMethod), new Expression[] { new EExternalConstant(LoggerFactory.getLogger(identifier), Logger), apply.parameters[0], apply.parameters[1] }); return apply; } }); addValue(value); } { // logging function with two parameters :: Marker -> String -> () JavaMethod javaMethod = new JavaMethod(false, "org/slf4j/Logger", methodName, Types.PROC, Types.UNIT, Logger, Marker, Types.STRING); SCLValue value = new SCLValue(Name.create(getName(), methodName + "M")); value.setType(loggingTypeWithMarker); value.setMacroRule(new MacroRule() { @Override public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) { String identifier = resolveModuleIdentifier(context.getCompilationContext()); apply.set(new ELiteral(javaMethod), new Expression[] { new EExternalConstant(LoggerFactory.getLogger(identifier), Logger), apply.parameters[0], apply.parameters[1] }); return apply; } }); addValue(value); } { // logging function with three parameters :: Marker -> String -> Throwable -> () JavaMethod javaMethod = new JavaMethod(false, "org/slf4j/Logger", methodName, Types.PROC, Types.UNIT, Logger, Marker, Types.STRING, Throwable); SCLValue value = new SCLValue(Name.create(getName(), methodName + "ME")); value.setType(loggingTypeWithMarkerAndException); value.setMacroRule(new MacroRule() { @Override public Expression apply(SimplificationContext context, Type[] typeParameters, EApply apply) { String identifier = resolveModuleIdentifier(context.getCompilationContext()); apply.set(new ELiteral(javaMethod), new Expression[] { new EExternalConstant(LoggerFactory.getLogger(identifier), Logger), apply.parameters[0], apply.parameters[1], apply.parameters[2] }); return apply; } }); addValue(value); } } setParentClassLoader(LoggerFactory.class.getClassLoader()); } private static String generateIsEnabledName(String level) { return "is" + capitalizeFirstCharacter(level) + "Enabled"; } private static String capitalizeFirstCharacter(String input) { return input.substring(0, 1).toUpperCase() + input.substring(1); } private static String resolveModuleIdentifier(CompilationContext context) { ConcreteModule module = context.module; String identifier; if (module != null) { String moduleName = module.getName(); if (moduleName.startsWith("http://")) { moduleName = moduleName.substring("http://".length()); } identifier = moduleName.replaceAll("/", "."); } else { identifier = CommandSession.class.getName(); } return identifier; } }