Skip to content

Commit 55a6154

Browse files
authored
Merge pull request #1015 from iRevive/core-logs/severity
core-logs: add Severity
2 parents 42bdbe4 + b9e65bd commit 55a6154

File tree

6 files changed

+360
-0
lines changed

6 files changed

+360
-0
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ lazy val `core-logs` = crossProject(JVMPlatform, JSPlatform, NativePlatform)
210210
.settings(munitDependencies)
211211
.settings(
212212
name := "otel4s-core-logs",
213+
startYear := Some(2025),
213214
libraryDependencies ++= Seq(
214215
"org.typelevel" %%% "cats-laws" % CatsVersion % Test,
215216
"org.typelevel" %%% "discipline-munit" % MUnitDisciplineVersion % Test
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright 2025 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.otel4s.logs
18+
19+
import cats.Hash
20+
import cats.Show
21+
22+
/** Represents the severity of a log record.
23+
*
24+
* @param value
25+
* smaller numerical values correspond to less severe events (such as debug events), larger numerical values
26+
* correspond to more severe events (such as errors and critical events)
27+
*
28+
* @param name
29+
* a human-readable name, see [[https://opentelemetry.io/docs/specs/otel/logs/data-model/#displaying-severity]]
30+
*
31+
* @see
32+
* [[https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber]]
33+
*/
34+
sealed abstract class Severity(val value: Int, val name: String) {
35+
36+
override final def hashCode(): Int =
37+
Hash[Severity].hash(this)
38+
39+
override final def equals(obj: Any): Boolean =
40+
obj match {
41+
case other: Severity => Hash[Severity].eqv(this, other)
42+
case _ => false
43+
}
44+
45+
override final def toString: String =
46+
Show[Severity].show(this)
47+
48+
}
49+
50+
object Severity {
51+
52+
/** A fine-grained debugging event. Typically disabled in default configurations.
53+
*/
54+
sealed abstract class Trace(value: Int, name: String) extends Severity(value, name)
55+
56+
/** A debugging event.
57+
*/
58+
sealed abstract class Debug(value: Int, name: String) extends Severity(value, name)
59+
60+
/** An informational event. Indicates that an event happened.
61+
*/
62+
sealed abstract class Info(value: Int, name: String) extends Severity(value, name)
63+
64+
/** A warning event. Not an error but is likely more important than an informational event.
65+
*/
66+
sealed abstract class Warn(value: Int, name: String) extends Severity(value, name)
67+
68+
/** An error event. Something went wrong.
69+
*/
70+
sealed abstract class Error(value: Int, name: String) extends Severity(value, name)
71+
72+
/** A fatal error such as application or system crash.
73+
*/
74+
sealed abstract class Fatal(value: Int, name: String) extends Severity(value, name)
75+
76+
/** A fine-grained debugging event. Typically disabled in default configurations.
77+
*/
78+
def trace: Trace = Trace1
79+
def trace2: Trace = Trace2
80+
def trace3: Trace = Trace3
81+
def trace4: Trace = Trace4
82+
83+
/** A debugging event.
84+
*/
85+
def debug: Debug = Debug1
86+
def debug2: Debug = Debug2
87+
def debug3: Debug = Debug3
88+
def debug4: Debug = Debug4
89+
90+
/** An informational event. Indicates that an event happened.
91+
*/
92+
def info: Info = Info1
93+
def info2: Info = Info2
94+
def info3: Info = Info3
95+
def info4: Info = Info4
96+
97+
/** A warning event. Not an error but is likely more important than an informational event.
98+
*/
99+
def warn: Warn = Warn1
100+
def warn2: Warn = Warn2
101+
def warn3: Warn = Warn3
102+
def warn4: Warn = Warn4
103+
104+
/** An error event. Something went wrong.
105+
*/
106+
def error: Error = Error1
107+
def error2: Error = Error2
108+
def error3: Error = Error3
109+
def error4: Error = Error4
110+
111+
/** A fatal error such as application or system crash.
112+
*/
113+
def fatal: Fatal = Fatal1
114+
def fatal2: Fatal = Fatal2
115+
def fatal3: Fatal = Fatal3
116+
def fatal4: Fatal = Fatal4
117+
118+
implicit val severityHash: Hash[Severity] = Hash.by(_.value)
119+
120+
/** @see
121+
* [[https://opentelemetry.io/docs/specs/otel/logs/data-model/#displaying-severity]]
122+
*/
123+
implicit val severityShow: Show[Severity] = Show.show(_.name)
124+
125+
private[otel4s] case object Trace1 extends Trace(1, "TRACE")
126+
private[otel4s] case object Trace2 extends Trace(2, "TRACE2")
127+
private[otel4s] case object Trace3 extends Trace(3, "TRACE3")
128+
private[otel4s] case object Trace4 extends Trace(4, "TRACE4")
129+
130+
private[otel4s] case object Debug1 extends Debug(5, "DEBUG")
131+
private[otel4s] case object Debug2 extends Debug(6, "DEBUG2")
132+
private[otel4s] case object Debug3 extends Debug(7, "DEBUG3")
133+
private[otel4s] case object Debug4 extends Debug(8, "DEBUG4")
134+
135+
private[otel4s] case object Warn1 extends Warn(13, "WARN")
136+
private[otel4s] case object Warn2 extends Warn(14, "WARN2")
137+
private[otel4s] case object Warn3 extends Warn(15, "WARN3")
138+
private[otel4s] case object Warn4 extends Warn(16, "WARN4")
139+
140+
private[otel4s] case object Info1 extends Info(9, "INFO")
141+
private[otel4s] case object Info2 extends Info(10, "INFO2")
142+
private[otel4s] case object Info3 extends Info(11, "INFO3")
143+
private[otel4s] case object Info4 extends Info(12, "INFO4")
144+
145+
private[otel4s] case object Error1 extends Error(17, "ERROR")
146+
private[otel4s] case object Error2 extends Error(18, "ERROR2")
147+
private[otel4s] case object Error3 extends Error(19, "ERROR3")
148+
private[otel4s] case object Error4 extends Error(20, "ERROR4")
149+
150+
private[otel4s] case object Fatal1 extends Fatal(21, "FATAL")
151+
private[otel4s] case object Fatal2 extends Fatal(22, "FATAL2")
152+
private[otel4s] case object Fatal3 extends Fatal(23, "FATAL3")
153+
private[otel4s] case object Fatal4 extends Fatal(24, "FATAL4")
154+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2025 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.otel4s.logs
18+
19+
import cats.Show
20+
import cats.kernel.laws.discipline.HashTests
21+
import munit.DisciplineSuite
22+
import org.scalacheck.Prop
23+
import org.typelevel.otel4s.logs.scalacheck.Arbitraries
24+
import org.typelevel.otel4s.logs.scalacheck.Cogens
25+
import org.typelevel.otel4s.logs.scalacheck.Gens
26+
27+
class SeveritySuite extends DisciplineSuite {
28+
import Cogens.severityCogen
29+
import Arbitraries.severityArbitrary
30+
31+
checkAll("Severity.HashLaws", HashTests[Severity].hash)
32+
33+
test("Show[Severity]") {
34+
Prop.forAll(Gens.severity) { severity =>
35+
assertEquals(Show[Severity].show(severity), severity.toString)
36+
}
37+
}
38+
39+
test("Severity values match the OpenTelemetry specification") {
40+
assertEquals(Severity.trace.value, 1)
41+
assertEquals(Severity.trace2.value, 2)
42+
assertEquals(Severity.trace3.value, 3)
43+
assertEquals(Severity.trace4.value, 4)
44+
assertEquals(Severity.debug.value, 5)
45+
assertEquals(Severity.debug2.value, 6)
46+
assertEquals(Severity.debug3.value, 7)
47+
assertEquals(Severity.debug4.value, 8)
48+
assertEquals(Severity.info.value, 9)
49+
assertEquals(Severity.info2.value, 10)
50+
assertEquals(Severity.info3.value, 11)
51+
assertEquals(Severity.info4.value, 12)
52+
assertEquals(Severity.warn.value, 13)
53+
assertEquals(Severity.warn2.value, 14)
54+
assertEquals(Severity.warn3.value, 15)
55+
assertEquals(Severity.warn4.value, 16)
56+
assertEquals(Severity.error.value, 17)
57+
assertEquals(Severity.error2.value, 18)
58+
assertEquals(Severity.error3.value, 19)
59+
assertEquals(Severity.error4.value, 20)
60+
assertEquals(Severity.fatal.value, 21)
61+
assertEquals(Severity.fatal2.value, 22)
62+
assertEquals(Severity.fatal3.value, 23)
63+
assertEquals(Severity.fatal4.value, 24)
64+
}
65+
66+
test("Severity toString match the OpenTelemetry specification") {
67+
assertEquals(Severity.trace.toString, "TRACE")
68+
assertEquals(Severity.trace2.toString, "TRACE2")
69+
assertEquals(Severity.trace3.toString, "TRACE3")
70+
assertEquals(Severity.trace4.toString, "TRACE4")
71+
assertEquals(Severity.debug.toString, "DEBUG")
72+
assertEquals(Severity.debug2.toString, "DEBUG2")
73+
assertEquals(Severity.debug3.toString, "DEBUG3")
74+
assertEquals(Severity.debug4.toString, "DEBUG4")
75+
assertEquals(Severity.info.toString, "INFO")
76+
assertEquals(Severity.info2.toString, "INFO2")
77+
assertEquals(Severity.info3.toString, "INFO3")
78+
assertEquals(Severity.info4.toString, "INFO4")
79+
assertEquals(Severity.warn.toString, "WARN")
80+
assertEquals(Severity.warn2.toString, "WARN2")
81+
assertEquals(Severity.warn3.toString, "WARN3")
82+
assertEquals(Severity.warn4.toString, "WARN4")
83+
assertEquals(Severity.error.toString, "ERROR")
84+
assertEquals(Severity.error2.toString, "ERROR2")
85+
assertEquals(Severity.error3.toString, "ERROR3")
86+
assertEquals(Severity.error4.toString, "ERROR4")
87+
assertEquals(Severity.fatal.toString, "FATAL")
88+
assertEquals(Severity.fatal2.toString, "FATAL2")
89+
assertEquals(Severity.fatal3.toString, "FATAL3")
90+
assertEquals(Severity.fatal4.toString, "FATAL4")
91+
}
92+
93+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2025 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.otel4s.logs.scalacheck
18+
19+
import org.scalacheck.Arbitrary
20+
import org.typelevel.otel4s.logs.Severity
21+
22+
trait Arbitraries {
23+
24+
implicit val severityArbitrary: Arbitrary[Severity] =
25+
Arbitrary(Gens.severity)
26+
27+
}
28+
29+
object Arbitraries extends Arbitraries
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2025 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.otel4s.logs.scalacheck
18+
19+
import org.scalacheck.Cogen
20+
import org.typelevel.otel4s.logs.Severity
21+
22+
trait Cogens {
23+
24+
implicit val severityCogen: Cogen[Severity] =
25+
Cogen[Int].contramap(_.value)
26+
27+
}
28+
29+
object Cogens extends Cogens
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2025 Typelevel
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.typelevel.otel4s.logs.scalacheck
18+
19+
import org.scalacheck.Gen
20+
import org.typelevel.otel4s.logs.Severity
21+
22+
trait Gens {
23+
24+
val severity: Gen[Severity] =
25+
Gen.oneOf(
26+
Severity.trace,
27+
Severity.trace2,
28+
Severity.trace3,
29+
Severity.trace4,
30+
Severity.debug,
31+
Severity.debug2,
32+
Severity.debug3,
33+
Severity.debug4,
34+
Severity.info,
35+
Severity.info2,
36+
Severity.info3,
37+
Severity.info4,
38+
Severity.warn,
39+
Severity.warn2,
40+
Severity.warn3,
41+
Severity.warn4,
42+
Severity.error,
43+
Severity.error2,
44+
Severity.error3,
45+
Severity.error4,
46+
Severity.fatal,
47+
Severity.fatal2,
48+
Severity.fatal3,
49+
Severity.fatal4,
50+
)
51+
52+
}
53+
54+
object Gens extends Gens

0 commit comments

Comments
 (0)