{"id":15289166,"url":"https://github.com/tersesystems/blindsight","last_synced_at":"2025-10-25T14:31:06.700Z","repository":{"id":39962941,"uuid":"247598951","full_name":"tersesystems/blindsight","owner":"tersesystems","description":"Blindsight is a Scala logging API with DSL based structured logging, fluent logging, semantic logging, flow logging, and context aware logging.","archived":false,"fork":false,"pushed_at":"2024-08-14T04:37:04.000Z","size":13738,"stargazers_count":86,"open_issues_count":40,"forks_count":6,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-02-03T15:52:08.276Z","etag":null,"topics":["logging","logstash-logback-encoder","scala","slf4j","slf4j-api","structured-logging","tracing"],"latest_commit_sha":null,"homepage":"https://tersesystems.github.io/blindsight/","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tersesystems.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-16T03:06:57.000Z","updated_at":"2024-12-18T12:38:18.000Z","dependencies_parsed_at":"2023-02-19T02:45:56.061Z","dependency_job_id":"82275fb7-810f-46b2-bf8e-e7d907505d17","html_url":"https://github.com/tersesystems/blindsight","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fblindsight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fblindsight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fblindsight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tersesystems%2Fblindsight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tersesystems","download_url":"https://codeload.github.com/tersesystems/blindsight/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238161514,"owners_count":19426669,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["logging","logstash-logback-encoder","scala","slf4j","slf4j-api","structured-logging","tracing"],"created_at":"2024-09-30T15:59:28.276Z","updated_at":"2025-10-25T14:31:06.060Z","avatar_url":"https://github.com/tersesystems.png","language":"Scala","readme":"# Blindsight\n\n\n[![Maven central](https://img.shields.io/badge/mavencentral-com.tersesystems.blindsight-blue.svg)](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.tersesystems.blindsight%22)\n[![License Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://www.tldrlegal.com/l/apache2)\n\n![Build](https://github.com/tersesystems/blindsight/actions/workflows/ci.yml/badge.svg)\n[![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)\n\nBlindsight is a logging library written in Scala that wraps SLF4J.  The name is taken from Peter Watts' excellent first contact novel, [Blindsight](https://en.wikipedia.org/wiki/Blindsight_\\(Watts_novel\\)).\n\nThe core feature of Blindsight is that it is \"type safe\" -- rather than passing in arguments of type `java.lang.Object`, the API accepts only objects that can be converted into an `Argument` through the `ToArgument` [type class](https://tersesystems.github.io/blindsight/usage/typeclasses.html).\n\n```\nval str: String = \"string arg\"\nval number: Int = 1\nval arg: Person = Person(name, age) // has a ToArgument[Person] type class instance\nlogger.info(\"{} {} {}\", bool, number, person) // compiles fine\n\nlogger.info(\"{}\", new Object()) // WILL NOT COMPILE\n```\n\nBy adding type safety, Blindsight gives the application more control over how data is logged, rather than implicitly relying on the `toString` method to render data for logging purposes.\n\nBlindsight adds [useful features](https://tersesystems.github.io/blindsight/usage/overview.html) that solve several outstanding problems with logging:\n\n* Rendering structured logs in multiple formats through an AST, along with an optional format-independent [DSL](https://tersesystems.github.io/blindsight/usage/dsl.html).\n* Providing thread-safe context to logs through [context aware logging](https://tersesystems.github.io/blindsight/usage/context.html).\n* Time-based and targeted logging through [conditional logging](https://tersesystems.github.io/blindsight/usage/conditional.html).\n* Dynamic targeted logging through [scripting](https://tersesystems.github.io/blindsight/usage/scripting.html).\n* Easier \"printf debugging\" through macro based [inspections](https://tersesystems.github.io/blindsight/usage/inspections.html).\n\nUsing Scala to break apart the SLF4J API also makes constructing new logging APIs much easier.  You have the option of creating your own, depending on your use case:\n\n* Building up complex logging statements through [fluent logging](https://tersesystems.github.io/blindsight/usage/fluent.html).\n* Enforcing user supplied type constraints through [semantic logging](https://tersesystems.github.io/blindsight/usage/semantic.html).\n* Minimal-overhead tracing and causality tracking through [flow logging](https://tersesystems.github.io/blindsight/usage/flow.html).\n* Managing complex relationships and schema through [JSON-LD](https://tersesystems.github.io/blindsight/usage/jsonld.html).\n\nFinally, there's also more advanced functionality to transform arguments and statements before entering SLF4J:\n\n* Resolving operation-specific loggers through [logger resolvers](https://tersesystems.github.io/blindsight/usage/resolvers.html).\n* Hooks into logging entries through [entry transformation](https://tersesystems.github.io/blindsight/usage/transform.html)\n* Application accessible debug and trace logs through [event buffers](https://tersesystems.github.io/blindsight/usage/buffer.html)\n\nSee [the documentation](https://tersesystems.github.io/blindsight/) for more details.\n\n## Blindsight and Echopraxia\n\nIf you are looking for a strict structured logging solution in Scala, please checkout [echopraxia-plusscala](https://github.com/tersesystems/echopraxia-plusscala).\n\nStructured logging is optional in Blindsight, and it's possible to mix structured and \"flat\" arguments and markers into a logging statement.  In contrast, [echopraxia-plusscala](https://github.com/tersesystems/echopraxia-plusscala) **requires** structured logging and does not allow unstructured data as input.\n\n## Example\n\nYou can check out a \"starter project\" at [https://github.com/tersesystems/blindsight-starter](https://github.com/tersesystems/blindsight-starter).\n\nThere's an example application at [https://github.com/tersesystems/play-blindsight](https://github.com/tersesystems/play-blindsight) that integrates with Honeycomb Tracing using the flow logger:\n\n![trace.png](trace.png)\n\n## Dependencies\n\nThe only hard dependency is the SLF4J API.  Structured logging is implemented for Logback with [logstash-logback-encoder](https://github.com/logstash/logstash-logback-encoder), but this is only a requirement if you are using structured logging.\n\nBlindsight is a pure SLF4J wrapper: it delegates all logging through to the SLF4J API and does not configure or manage the SLF4J implementation at all.\n\nVersions are published for Scala 2.11, 2.12, 2.13, and 3.0.0.\n\n## Install\n\nSee [Setup](https://tersesystems.github.io/blindsight/setup/index.html) for how to install Blindsight.\n\nYou can check out a \"starter project\" at [https://github.com/tersesystems/blindsight-starter](https://github.com/tersesystems/blindsight-starter).\n\nBecause Blindsight uses a very recent version of Logstash that depends on Jackson 2.11.0, you may need to update your dependencies for the `jackson-scala-module` if you're using Play or Akka.\n\n```\nlibraryDependencies += \"com.fasterxml.jackson.module\" %% \"jackson-module-scala\" % \"2.11.0\"\n```\n\n## Usage\n \nThe easiest way to use Blindsight is to import the base package and the DSL:\n\n```scala\nimport com.tersesystems.blindsight._\nimport com.tersesystems.blindsight.DSL._\n```\n\nTo use a Blindsight Logger:\n\n```scala\nval logger = LoggerFactory.getLogger\nlogger.info(\"I am an SLF4J-like logger\")\n```\n\nor in block form for diagnostic logging:\n\n```scala\nlogger.debug { debug =\u003e debug(\"I am an SLF4J-like logger\") }\n```\n\n[Structured DSL](https://tersesystems.github.io/blindsight/usage/dsl.html):\n\n```scala\nimport com.tersesystems.blindsight._\nimport com.tersesystems.blindsight.DSL._\n\nlogger.info(\"Logs with argument {}\", bobj(\"array\" -\u003e Seq(\"one\", \"two\", \"three\")))\n```\n\n[Statement Interpolation](https://tersesystems.github.io/blindsight/usage/interpolation.html): \n\n```scala\nval dayOfWeek = \"Monday\"\nval temp = 72 \n\n// macro expands this to:\n// Statement(\"It is {} and the temperature is {} degrees.\", Arguments(dayOfWeek, temp))\nval statement: Statement = st\"It is ${dayOfWeek} and the temperature is ${temp} degrees.\"\n\nlogger.info(statement)\n```\n\n[Marker/Argument Type Classes](https://tersesystems.github.io/blindsight/usage/typeclass.html):\n \n```scala\ncase class Lotto(\n  id: Long,\n  winningNumbers: List[Int],\n  winners: List[Winner],\n  drawDate: Option[java.util.Date]\n) {\n  lazy val asBObject: BObject = \"lotto\" -\u003e\n      (\"lotto-id\"          -\u003e id) ~\n        (\"winning-numbers\" -\u003e winningNumbers) ~\n        (\"draw-date\"       -\u003e drawDate.map(_.toString)) ~\n        (\"winners\"         -\u003e winners.map(w =\u003e w.asBObject))\n}\n\nobject Lotto {\n  implicit val toArgument: ToArgument[Lotto] = ToArgument { lotto =\u003e Argument(lotto.asBObject) }\n}\n\nval winners =\n  List(Winner(23, List(2, 45, 34, 23, 3, 5)), Winner(54, List(52, 3, 12, 11, 18, 22)))\nval lotto = Lotto(5, List(2, 45, 34, 23, 7, 5, 3), winners, None)\n\nlogger.info(\"message {}\", lotto) // auto-converted to structured output\n```\n\n[JSON-LD](https://tersesystems.github.io/blindsight/usage/jsonld.html):\n\n```scala\nimplicit val nodeObjectToArgument: ToArgument[NodeObject] = ToArgument[NodeObject] { nodeObject =\u003e\n  Argument(BlindsightASTMapping.toBObject(nodeObject))\n}\n\nimplicit val nodeObjectToMarkers: ToMarkers[NodeObject] = ToMarkers { nodeObject =\u003e\n  Markers(BlindsightASTMapping.toBObject(nodeObject))\n}\n\nimplicit val nodeObjectToStatement: ToStatement[NodeObject] = ...\n\nclass Foo extends LDContext { // LDContext contains all the type safe bindings\n  def sayHello(): Unit = {\n    val willPerson = NodeObject(\n      `@type`    -\u003e \"Person\",\n      `@id`      -\u003e willId,\n      givenName  -\u003e \"Will\",\n      familyName -\u003e \"Sargent\",\n      parent     -\u003e parentId,\n      occupation -\u003e Occupation(\n        estimatedSalary = MonetaryAmount(Currency.getInstance(\"USD\"), 1),\n        name = \"Code Monkey\"\n      )\n    )\n\n    logger.info(\"as an argument {}\", willPerson) // as an argument\n    logger.info(Markers(willPerson), \"as a marker\") // as a marker\n    \n    logger.semantic[NodeObject].info(willPerson) // or as a statement\n  }\n}\n```\n\n[Fluent logging](https://tersesystems.github.io/blindsight/usage/fluent.html):\n\n```scala\nlogger.fluent.info\n  .message(\"The Magic Words are\")\n  .argument(Arguments(\"Squeamish\", \"Ossifrage\"))\n  .logWithPlaceholders()\n```\n\n[Semantic logging](https://tersesystems.github.io/blindsight/usage/semantic.html):\n\n```scala\n// log only user events\nlogger.semantic[UserEvent].info(userEvent)\n\n// Works well with refinement types\nimport eu.timepit.refined.api.Refined\nimport eu.timepit.refined.string._\nimport eu.timepit.refined._\nlogger.semantic[String Refined Url].info(refineMV(Url)(\"https://tersesystems.com\"))\n```\n\n[Flow logging](https://tersesystems.github.io/blindsight/usage/flow.html):\n\n```scala\nimport com.tersesystems.blindsight.flow._\n\nimplicit def flowBehavior[B]: FlowBehavior[B] = new SimpleFlowBehavior\n\nval arg1: Int = 1\nval arg2: Int = 2\nval result:Int = logger.flow.trace(arg1 + arg2)\n```\n\n[Conditional logging](https://tersesystems.github.io/blindsight/usage/conditional.html):\n\n```scala\nlogger.withCondition(booleanCondition).info(\"Only logs when condition is true\")\n\nlogger.info.when(booleanCondition) { info =\u003e info(\"when true\") }\n```\n\n[Context aware logging](https://tersesystems.github.io/blindsight/usage/context.html):\n\n```scala\nimport DSL._\n\n// Add key/value pairs with DSL and return a logger\nval markerLogger = logger.withMarker(bobj(\"userId\" -\u003e userId))\n\n// log with generated logger\nmarkerLogger.info(\"Logging with user id added as a context marker!\")\n\n// can retrieve state markers\nval contextMarkers: Markers = markerLogger.markers\n```\n\n[Entry Transformation](https://tersesystems.github.io/blindsight/usage/transform.html):\n\n```scala\nval logger = LoggerFactory.getLogger\n               .withEntryTransform(e =\u003e e.copy(message = e.message + \" IN BED\"))\n\nlogger.info(\"You will discover your hidden talents\")\n```\n\n[Event Buffer](https://tersesystems.github.io/blindsight/usage/buffer.html):\n\n```scala\nval queueBuffer = EventBuffer(1)\nval logger      = LoggerFactory.getLogger.withEventBuffer(queueBuffer)\n\nlogger.info(\"Hello world\")\n\nval event = queueBuffer.head\n```\n\n[Scripting](https://tersesystems.github.io/blindsight/usage/scripting.html):\n\n```scala\nval scriptHandle = new ScriptHandle {\n  override def isInvalid: Boolean = false // on file modification, etc\n  override val script: String =\n    \"\"\"import strings as s from 'std.tf';\n      |alias s.ends_with? as ends_with?;\n      |\n      |library blindsight {\n      |  function evaluate: (long level, string enc, long line, string file) -\u003e\n      |    if (ends_with?(enc, \"specialMethodName\")) then true\n      |    else false;\n      |}\n      |\"\"\".stripMargin\n  override def report(e: Throwable): Unit = e.printStackTrace()\n}\nval scriptManager = new ScriptManager(scriptHandle) \nval location = new ScriptAwareLocation(scriptManager)\n\ndef specialMethodName = {\n  // inside the specialMethodName method here :-)\n  logger.debug.when(location.here) { log =\u003e \n    log(\"script allows selective logging by method or by line\")\n  }\n}\n```\n\n[Inspections](https://tersesystems.github.io/blindsight/usage/inspections.html):\n\n```scala\nimport com.tersesystems.blindsight.inspection.InspectionMacros._\n\ndecorateIfs(dif =\u003e logger.debug(s\"${dif.code} = ${dif.result}\")) {\n  if (System.currentTimeMillis() % 17 == 0) {\n    println(\"branch 1\")\n  } else if (System.getProperty(\"derp\") == null) {\n    println(\"branch 2\")\n  } else {\n    println(\"else branch\")\n  }\n}\n```\n\n## Benchmarks\n\nBenchmarks are available [here](https://tersesystems.github.io/blindsight/benchmarks.html).\n\n## License\n\nBlindsight is released under the \"Apache 2\" license. See [LICENSE](LICENSE) for specifics and copyright declaration.\n","funding_links":[],"categories":["Table of Contents"],"sub_categories":["Extensions"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftersesystems%2Fblindsight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftersesystems%2Fblindsight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftersesystems%2Fblindsight/lists"}