Initial upload
This commit is contained in:
22
elixir/log-level/.exercism/config.json
Normal file
22
elixir/log-level/.exercism/config.json
Normal 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."
|
||||
}
|
1
elixir/log-level/.exercism/metadata.json
Normal file
1
elixir/log-level/.exercism/metadata.json
Normal 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}
|
4
elixir/log-level/.formatter.exs
Normal file
4
elixir/log-level/.formatter.exs
Normal 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
24
elixir/log-level/.gitignore
vendored
Normal 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
75
elixir/log-level/HELP.md
Normal 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
20
elixir/log-level/HINTS.md
Normal 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
|
82
elixir/log-level/README.md
Normal file
82
elixir/log-level/README.md
Normal 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
|
23
elixir/log-level/lib/log_level.ex
Normal file
23
elixir/log-level/lib/log_level.ex
Normal 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
28
elixir/log-level/mix.exs
Normal 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
|
101
elixir/log-level/test/log_level_test.exs
Normal file
101
elixir/log-level/test/log_level_test.exs
Normal 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
|
2
elixir/log-level/test/test_helper.exs
Normal file
2
elixir/log-level/test/test_helper.exs
Normal file
@@ -0,0 +1,2 @@
|
||||
ExUnit.start()
|
||||
ExUnit.configure(exclude: :pending, trace: true, seed: 0)
|
Reference in New Issue
Block a user