Initial upload

This commit is contained in:
2022-08-24 14:28:45 +02:00
parent c67653ddee
commit 57bc7b0289
370 changed files with 18479 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
{
"authors": [
"neenjaw"
],
"files": {
"solution": [
"lib/log_level.ex"
],
"test": [
"test/log_level_test.exs"
],
"exemplar": [
".meta/exemplar.ex"
]
},
"language_versions": ">=1.10",
"forked_from": [
"csharp/logs-logs-logs"
],
"icon": "log-levels",
"blurb": "Learn about atoms and the cond conditional expression by aggregating application logs."
}

View File

@@ -0,0 +1 @@
{"track":"elixir","exercise":"log-level","id":"cfabdd7d8b3045aa9515e5fcc87ffaf6","url":"https://exercism.org/tracks/elixir/exercises/log-level","handle":"halfdan","is_requester":true,"auto_approve":false}

View File

@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

24
elixir/log-level/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
cond-*.tar

75
elixir/log-level/HELP.md Normal file
View File

@@ -0,0 +1,75 @@
# Help
## Running the tests
From the terminal, change to the base directory of the exercise then execute the tests with:
```bash
$ mix test
```
This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`
Documentation:
* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)
## Pending tests
In test suites of practice exercises, all but the first test have been tagged to be skipped.
Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.
For example:
```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```
If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:
```bash
$ mix test --include pending
```
Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.
```elixir
# ExUnit.configure(exclude: :pending, trace: true)
```
## Useful `mix test` options
* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
* `--failed` - runs only tests that failed the last time they ran
* `--max-failures` - the suite stops evaluating tests when this number of test failures
is reached
* `--seed 0` - disables randomization so the tests in a single file will always be ran
in the same order they were defined in
## Submitting your solution
You can submit your solution using the `exercism submit lib/log_level.ex` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
If you can't find what you're looking for in the documentation, feel free to ask help in the Exercism's BEAM [gitter channel](https://gitter.im/exercism/xerlang).

20
elixir/log-level/HINTS.md Normal file
View File

@@ -0,0 +1,20 @@
# Hints
## General
- The [atom type is described here][atom].
## 1. Return the logging code label
- You can use the [`cond/1` special form][cond] to elegantly handle the various log codes.
- You can use [equality operators][equality] to compare integers for strict type equality.
- There is a [way to specify a default branch][cond] in a cond expression that can be used to catch unspecified cases.
## 2. Send an alert
- You can use the [`cond/1` special form][cond] to decide if an alert should be sent.
- You can use [equality operators][equality] to compare atoms for equality.
[equality]: https://elixir-lang.org/getting-started/basic-operators.html
[atom]: https://elixir-lang.org/getting-started/basic-types.html#atoms
[cond]: https://elixir-lang.org/getting-started/case-cond-and-if.html#cond

View File

@@ -0,0 +1,82 @@
# Log Level
Welcome to Log Level on Exercism's Elixir Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
## Atoms
Elixir's `atom` type represents a fixed constant. An atom's value is simply its own name. This gives us a type-safe way to interact with data. Atoms can be defined as follows:
```elixir
# All atoms are preceded with a ':' then follow with alphanumeric snake-cased characters
variable = :an_atom
```
_Atoms_ are internally represented by an integer in a lookup table, which are set automatically. It is not possible to change this internal value.
## Cond
Often, we want to write code that can branch based on a condition. While there are many ways to do this in Elixir, one of the simplest ways is using `cond/1`.
At its simplest, `cond` follows the first path that evaluates to `true` with one or more branches:
```elixir
cond do
x > 10 -> :this_might_be_the_way
y < 7 -> :or_that_might_be_the_way
true -> :this_is_the_default_way
end
```
If no path evaluates to `true`, an error is raised by the runtime.
## Instructions
You are running a system that consists of a few applications producing many logs. You want to write a small program that will aggregate those logs and give them labels according to their severity level. All applications in your system use the same log codes, but some of the legacy applications don't support all the codes.
| Log code | Log label | Supported in legacy apps? |
| -------- | --------- | ------------------------- |
| 0 | trace | no |
| 1 | debug | yes |
| 2 | info | yes |
| 3 | warning | yes |
| 4 | error | yes |
| 5 | fatal | no |
| ? | unknown | - |
## 1. Return the logging code label
Implement the `LogLevel.to_label/2` function. It should take an integer code and a boolean flag telling you if the log comes from a legacy app, and return the label of a log line as an atom. Unknown log codes and codes unsupported in a legacy app should return an _unknown_ label.
```elixir
LogLevel.to_label(0, false)
# => :trace
LogLevel.to_label(0, true)
# => :unknown
```
## 2. Send an alert
Somebody has to be notified when unexpected things happen.
Implement the `LogLevel.alert_recipient/2` function to determine to whom the alert needs to be sent. The function should take an integer code and a boolean flag telling you if the log comes from a legacy app, and return the name of the recipient as an atom.
If the log label is _error_ or _fatal_, send the alert to the _ops_ team. If you receive a log with an _unknown_ label from a legacy system, send the alert to the _dev1_ team, other unknown labels should be sent to the _dev2_ team. All other log labels can be safely ignored.
```elixir
LogLevel.alert_recipient(-1, true)
# => :dev1
LogLevel.alert_recipient(0, false)
# => false
```
## Source
### Created by
- @neenjaw

View File

@@ -0,0 +1,23 @@
defmodule LogLevel do
def to_label(level, legacy?) do
cond do
level==0 && !legacy? -> :trace
level==1 && legacy? -> :debug
level==2 && legacy? -> :info
level==3 && legacy? -> :warning
level==4 && legacy? -> :error
level==5 && !legacy? -> :fatal
true -> :unknown
end
end
def alert_recipient(level, legacy?) do
lbl = to_label(level, legacy?)
cond do
lbl == :fatal || lbl == :error -> :ops
lbl == :unknown && legacy? -> :dev1
lbl == :unknown -> :dev2
true -> false
end
end
end

28
elixir/log-level/mix.exs Normal file
View File

@@ -0,0 +1,28 @@
defmodule LogLevel.MixProject do
use Mix.Project
def project do
[
app: :log_level,
version: "0.1.0",
# elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
]
end
end

View File

@@ -0,0 +1,101 @@
defmodule LogLevelTest do
use ExUnit.Case
describe "LogLevel.to_label/2" do
@tag task_id: 1
test "level 0 has label trace only in a non-legacy app" do
assert LogLevel.to_label(0, false) == :trace
assert LogLevel.to_label(0, true) == :unknown
end
@tag task_id: 1
test "level 1 has label debug" do
assert LogLevel.to_label(1, false) == :debug
assert LogLevel.to_label(1, true) == :debug
end
@tag task_id: 1
test "level 2 has label info" do
assert LogLevel.to_label(2, false) == :info
assert LogLevel.to_label(2, true) == :info
end
@tag task_id: 1
test "level 3 has label warning" do
assert LogLevel.to_label(3, false) == :warning
assert LogLevel.to_label(3, true) == :warning
end
@tag task_id: 1
test "level 4 has label error" do
assert LogLevel.to_label(4, false) == :error
assert LogLevel.to_label(4, true) == :error
end
@tag task_id: 1
test "level 5 has label fatal only in a non-legacy app" do
assert LogLevel.to_label(5, false) == :fatal
assert LogLevel.to_label(5, true) == :unknown
end
@tag task_id: 1
test "level 6 has label unknown" do
assert LogLevel.to_label(6, false) == :unknown
assert LogLevel.to_label(6, true) == :unknown
end
@tag task_id: 1
test "level -1 has label unknown" do
assert LogLevel.to_label(-1, false) == :unknown
assert LogLevel.to_label(-1, true) == :unknown
end
end
describe "LogLevel.alert_recipient/2" do
@tag task_id: 2
test "fatal code sends alert to ops" do
assert LogLevel.alert_recipient(5, false) == :ops
end
@tag task_id: 2
test "error code sends alert to ops" do
assert LogLevel.alert_recipient(4, false) == :ops
assert LogLevel.alert_recipient(4, true) == :ops
end
@tag task_id: 2
test "unknown code sends alert to dev team 1 for a legacy app" do
assert LogLevel.alert_recipient(6, true) == :dev1
assert LogLevel.alert_recipient(0, true) == :dev1
assert LogLevel.alert_recipient(5, true) == :dev1
end
@tag task_id: 2
test "unknown code sends alert to dev team 2" do
assert LogLevel.alert_recipient(6, false) == :dev2
end
@tag task_id: 2
test "trace code does not send alert" do
refute LogLevel.alert_recipient(0, false)
end
@tag task_id: 2
test "debug code does not send alert" do
refute LogLevel.alert_recipient(1, false)
refute LogLevel.alert_recipient(1, true)
end
@tag task_id: 2
test "info code does not send alert" do
refute LogLevel.alert_recipient(2, false)
refute LogLevel.alert_recipient(2, true)
end
@tag task_id: 2
test "warning code does not send alert" do
refute LogLevel.alert_recipient(3, false)
refute LogLevel.alert_recipient(3, true)
end
end
end

View File

@@ -0,0 +1,2 @@
ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true, seed: 0)