<commit_before>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wvlet.log

import java.io.{ByteArrayOutputStream, PrintStream, PrintWriter, StringWriter}
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, SignStyle}
import java.time.{Instant, ZoneId, ZonedDateTime}
import java.util.logging.Formatter
import java.util.regex.Pattern
import java.util.{Locale, logging => jl}

import wvlet.log.LogLevel.{DEBUG, ERROR, INFO, TRACE, WARN}

/**
  * To implement your own log formatter, implement this formatLog(r: LogRecord) method
  */
trait LogFormatter extends Formatter {
  def formatLog(r: LogRecord): String

  override def format(record: jl.LogRecord): String = {
    record match {
      case lr: LogRecord => formatLog(lr)
      case _ => formatLog(LogRecord(record))
    }
  }
}

object LogFormatter {

  import java.time.temporal.ChronoField._

  val systemZone             = ZoneId.systemDefault().normalized()
  val noSpaceTimestampFormat = new DateTimeFormatterBuilder()
                               .parseCaseInsensitive()
                               .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
                               .appendLiteral('-')
                               .appendValue(MONTH_OF_YEAR, 2)
                               .appendLiteral('-')
                               .appendValue(DAY_OF_MONTH, 2)
                               .appendLiteral('T')
                               .appendValue(HOUR_OF_DAY, 2)
                               .appendLiteral(':')
                               .appendValue(MINUTE_OF_HOUR, 2)
                               .appendLiteral(':')
                               .appendValue(SECOND_OF_MINUTE, 2)
                               .appendLiteral('.')
                               .appendValue(MILLI_OF_SECOND, 3)
                               .appendOffset("+HHMM", "Z")
                               .toFormatter(Locale.US)

  val humanReadableTimestampFormatter = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
                                        .appendLiteral('-')
                                        .appendValue(MONTH_OF_YEAR, 2)
                                        .appendLiteral('-')
                                        .appendValue(DAY_OF_MONTH, 2)
                                        .appendLiteral(' ')
                                        .appendValue(HOUR_OF_DAY, 2)
                                        .appendLiteral(':')
                                        .appendValue(MINUTE_OF_HOUR, 2)
                                        .appendLiteral(':')
                                        .appendValue(SECOND_OF_MINUTE, 2)
                                        .appendOffset("+HHMM", "Z")
                                        .toFormatter(Locale.US)

  def formatTimestamp(timeMillis: Long, dateTimeformatter:DateTimeFormatter = humanReadableTimestampFormatter): String = {
    val timestamp = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), systemZone)
    dateTimeformatter.format(timestamp)
  }

  def currentThreadName: String = Thread.currentThread().getName

  private val testFrameworkFilter = Pattern.compile("""\s+at (sbt\.|org\.scalatest\.).*""")
  val DEFAULT_STACKTRACE_FILTER : String => Boolean = { line:String =>
    !testFrameworkFilter.matcher(line).matches()
  }
  private var stackTraceFilter : String => Boolean = DEFAULT_STACKTRACE_FILTER

  /**
    * Set stack trace line filter
    * @param filter
    */
  def setStackTraceFilter(filter: String => Boolean) {
    stackTraceFilter = filter
  }

  def formatStacktrace(e: Throwable) : String = {
    val trace = new StringWriter()
    e.printStackTrace(new PrintWriter(trace))
    val stackTrace = trace.toString
    val filtered =
      stackTrace.split("\n")
      .filter(stackTraceFilter)
      .sliding(2)
      .collect { case Array(a, b) if a != b => a }
<commit_msg>Add comment<commit_after>/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wvlet.log

import java.io.{ByteArrayOutputStream, PrintStream, PrintWriter, StringWriter}
import java.time.format.{DateTimeFormatter, DateTimeFormatterBuilder, SignStyle}
import java.time.{Instant, ZoneId, ZonedDateTime}
import java.util.logging.Formatter
import java.util.regex.Pattern
import java.util.{Locale, logging => jl}

import wvlet.log.LogLevel.{DEBUG, ERROR, INFO, TRACE, WARN}

/**
  * To implement your own log formatter, implement this formatLog(r: LogRecord) method
  */
trait LogFormatter extends Formatter {
  def formatLog(r: LogRecord): String

  override def format(record: jl.LogRecord): String = {
    record match {
      case lr: LogRecord => formatLog(lr)
      case _ => formatLog(LogRecord(record))
    }
  }
}

object LogFormatter {

  import java.time.temporal.ChronoField._

  val systemZone             = ZoneId.systemDefault().normalized()
  val noSpaceTimestampFormat = new DateTimeFormatterBuilder()
                               .parseCaseInsensitive()
                               .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
                               .appendLiteral('-')
                               .appendValue(MONTH_OF_YEAR, 2)
                               .appendLiteral('-')
                               .appendValue(DAY_OF_MONTH, 2)
                               .appendLiteral('T')
                               .appendValue(HOUR_OF_DAY, 2)
                               .appendLiteral(':')
                               .appendValue(MINUTE_OF_HOUR, 2)
                               .appendLiteral(':')
                               .appendValue(SECOND_OF_MINUTE, 2)
                               .appendLiteral('.')
                               .appendValue(MILLI_OF_SECOND, 3)
                               .appendOffset("+HHMM", "Z")
                               .toFormatter(Locale.US)

  val humanReadableTimestampFormatter = new DateTimeFormatterBuilder()
                                        .parseCaseInsensitive()
                                        .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
                                        .appendLiteral('-')
                                        .appendValue(MONTH_OF_YEAR, 2)
                                        .appendLiteral('-')
                                        .appendValue(DAY_OF_MONTH, 2)
                                        .appendLiteral(' ')
                                        .appendValue(HOUR_OF_DAY, 2)
                                        .appendLiteral(':')
                                        .appendValue(MINUTE_OF_HOUR, 2)
                                        .appendLiteral(':')
                                        .appendValue(SECOND_OF_MINUTE, 2)
                                        .appendOffset("+HHMM", "Z")
                                        .toFormatter(Locale.US)

  def formatTimestamp(timeMillis: Long, dateTimeformatter:DateTimeFormatter = humanReadableTimestampFormatter): String = {
    val timestamp = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timeMillis), systemZone)
    dateTimeformatter.format(timestamp)
  }

  def currentThreadName: String = Thread.currentThread().getName

  private val testFrameworkFilter = Pattern.compile("""\s+at (sbt\.|org\.scalatest\.).*""")
  val DEFAULT_STACKTRACE_FILTER : String => Boolean = { line:String =>
    !testFrameworkFilter.matcher(line).matches()
  }
  private var stackTraceFilter : String => Boolean = DEFAULT_STACKTRACE_FILTER

  /**
    * Set stack trace line filter
    * @param filter
    */
  def setStackTraceFilter(filter: String => Boolean) {
    stackTraceFilter = filter
  }

  def formatStacktrace(e: Throwable) : String = {
    val trace = new StringWriter()
    e.printStackTrace(new PrintWriter(trace))
    val stackTrace = trace.toString
    val filtered =
      stackTrace.split("\n") // Array
      .filter(stackTraceFilter)
      .sliding(2)
      .collect { case Array(a, b) if a != b => a }

{'fraction_non_alphanumeric': 0.09189366590088612, 'fraction_numerical': 0.0048134777376654635, 'mean_word_length': 1.7322175732217573, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2397007', 'n_tokens_mistral': 2294, 'n_tokens_neox': 2235, 'n_words': 552}