0

Generally, we are looking to create a logging framework that can target human readable output as well as various structured data formats. So, a goal is minimizing code duplication in packaging the format functions. We created a library of functions that can be used to format log entries.

The client can then iterate a list of these as it processes each log record, in order to build the output string based on an arbitrary recipe.

for (LogFormatSteps logFormatStep : logFormatList {    

      sb = logFormatStep.getRecipeStep().f(sb, record, altContent);
    }

In discussions of Lambdas, they seem to be declared each in their own class extending their functional interface. This is a lot of overhead, javadocs notwithstanding. Is it going to be a problem if the lambdas are instead collected into an enumeration, as a way of reducing the number of classes that must ultimately be defined?

import java.util.Date;
import java.util.logging.LogRecord;

@FunctionalInterface
/** Appends stringbuilder with log information. */
interface LogFormatLambda {

  /**
   * Adds information to log entry text.
   *
   * @param logFragmentParam starting log fragment
   * @param logRecordParam which may be consulted by lambda
   * @param altContentParam which may be converted via toString() for use by lambda.
   */
  public abstract StringBuilder apply(
      StringBuilder logFragmentParam, LogRecord logRecordParam, Object altContentParam);
}

/** Formatter functions, in lambda notation, used by this formatter */
enum LogFormatFunctions {

  /** Append urrent date and time. */
  DATE((s, r, a) -> s.append(new Date(r.getMillis())).append(" ")),

  /** Append log level. */
  LEVEL((s, r, a) -> s.append(r.getLevel().getLocalizedName())),

  /** Append blank line. */
  LINE((s, r, a) -> s.append(System.getProperty("line.separator"))),

  /** Append class name, or, lacking that, log name. */
  CLASS(
      (s, r, a) ->
          (r.getSourceClassName() != null)
              ? s.append("     ").append(r.getSourceClassName())
              : (r.getLoggerName() != null) ? s.append("     ").append(r.getLoggerName()) : s),

  /** Append name of method generating the log entry. */
  METHOD(
      (s, r, a) ->
          (r.getSourceMethodName() != null)
              ? s.append("     ").append(r.getSourceMethodName())
              : s),

  /**
   * Append message text for the log entry, adding an indent.
   */
  MESSAGE(
      (s, r, a) ->
          (a != null) ? s.append("     ").append(a.toString().replace("\n", "\n     ")) : s);


  /** Lambda field. */
  LogFormatLambda f;

  /**
   * Constructor -loads lambdas.
   *
   * @param functionParam lambda for this process step
   */
  LogFormatFunctions(LogFormatLambda functionParam) {
    this.f = functionParam;
  }

  /**
   * Appends log information.
   *
   * @param stringBuilderParam current log entry fragment
   * @param logRecordParam log record (model) 
   * @param altContentParam object, provides toString() info to process steps 
   */
  StringBuilder f(
      StringBuilder stringBuilderParam, LogRecord logRecordParam, Object altContentParam) {

    return f.apply(stringBuilderParam, logRecordParam, altContentParam);
  }
}

That said, are there other ways of organizing lambdas, e.g., via naming conventions package organization, etc., to avoid verbosity?

I did see posts about using lambdas to process enumerations, and enumerations to control dynamic linking of lambdas as strategy objects. But so far I haven't seen one that addressed the viability of using an enumeration for purposes of reducing clutter.

3
  • 3
    that just looks like line noise at first glance, and second glance and third glance whomever is reading it and maintaining it will just rewrite it to be less obfuscated when they do take the time to figure out what it does. lambdas like everything else are easily abused when they are used as a hammer to make everything a nail. *see also: regexes and list comprehensions ( python ) and abusing functional idioms in non-functional languages ( as this is basically doing ).
    – user7519
    Feb 14, 2019 at 3:12
  • 2
    Using an enum prevents you from you or anyone else being able to add additional LogFormatFunctions later without directly having access to the enum itself. If you don't think anyone could possibly require an additional method, then this is fine. Otherwise, don't take this route. If anything, you can use static instances representing common methods, with the possibility of providing your own as well.
    – Neil
    Feb 14, 2019 at 6:38
  • it is helpful to know what is seen as obfuscation because the goal is to eliminate that. What about using constants e.g. LogFormatLambda DATE = (s ,r ,a) -> {...};? Then an event format recipe could be expressed as an array LogFormatLambda[] RECIPE = { DATE, LEVEL, MESSAGE; }?
    – John
    Feb 14, 2019 at 20:18

2 Answers 2

2

lambdas are just syntactic sugar, they generate the exact same byte code that anonymous inner classes would, as in they each result in a implementation of a class with a single method that compiles to a separate file. This obfuscation you have introduced does nothing to solve the "problem" you are trying to solve. Which is not really a problem.

Do you realize that enum can implement an interface and that would be a better way of doing this from an encapsulation standpoint if nothing else. then you do not even need lambda obfuscation you just implement the method on each new instance directly.

2

In Java, the idiomatic way to group a bunch of functions is just a plain old class with plain old methods. Then your logFormatList looks something like List.of(LogFormatFunctions::date, LogFormatFunctions::level).

If you really want something more concise than that, my personal preference would be some sort of format string, like "%d %l: %m" that you then parse to dynamically create your List<LogFormatLambda>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.