Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ Please note that this project is released with a [Contributor Code of Conduct](C

## **How to Contribute**

## Go Version Policy

- The repository supports Go 1.19+ (README).
- CI workflows use **Go 1.25** (`.github/workflows/pr-tests.yml` sets `go-version: 1.25.0`).
- Some modules declare higher Go versions due to dependencies:
- Go 1.24 (`golang.org/x/sys v0.37.0` requires 1.24)
- Contributors using Go versions ≥1.19 should be aware that CI may run a newer Go version, which could produce warnings if older syntax or deprecated features are used.

### **Submitting a Solution**

You can submit solutions to both Classic and Package challenges:
Expand Down
75 changes: 75 additions & 0 deletions packages/logrus/challenge-1-basic-logging-and-levels/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Challenge 1: Basic Logging & Levels

Build a simple **Logbook Application** that demonstrates fundamental logging concepts using the `logrus` library. This challenge will teach you how to set up a logger, use different log levels, and control log output format.

## Challenge Requirements

Create a simple Go application that simulates adding entries to a logbook. The application must:

1. **Initialize Logrus**: Set up a global `logrus` logger instance
2. **Use Different Log Levels**: Implement a function that logs messages at various severity levels: `Debug`, `Info`, `Warn`, `Error`, `Fatal`, and `Panic`
3. **Control Log Output Level**: The application should be configurable to show logs above a certain severity. For example, setting the level to `info` should hide `debug` messages
4. **Format Logs**: Configure the logger to output logs in a structured `JSON` format

## How It Should Work

You will build a simple `runLogbookOperations` function that simulates a logbook's daily operations. The application's logging output will be controlled by setting the log level.

### Sample Output

**When the log level is set to `info`:**
The output should only include `info`, `warn`, `error`, and `fatal` messages. The `fatal` log will terminate the application, so `panic` will not be reached.

```json
{"level":"info","msg":"Logbook application starting up.","time":"2025-10-02T17:30:00+05:30"}
{"level":"info","msg":"Opening today's log entry.","time":"2025-10-02T17:30:00+05:30"}
{"level":"warning","msg":"Disk space is running low.","time":"2025-10-02T17:30:00+05:30"}
{"level":"error","msg":"Failed to connect to remote backup service.","time":"2025-10-02T17:30:00+05:30"}
{"level":"fatal","msg":"Critical configuration file 'config.yml' not found.","time":"2025-10-02T17:30:00+05:30"}
```
### When the log level is set to `debug`

The output should include all messages from `debug` up to `fatal`.

```json
{"level":"debug","msg":"Checking system status...","time":"2025-10-02T17:30:00+05:30"}
{"level":"debug","msg":"Memory usage: 256MB","time":"2025-10-02T17:30:00+05:30"}
{"level":"info","msg":"Logbook application starting up.","time":"2025-10-02T17:30:00+05:30"}
{"level":"info","msg":"Opening today's log entry.","time":"2025-10-02T17:30:00+05:30"}
{"level":"warning","msg":"Disk space is running low.","time":"2025-10-02T17:30:00+05:30"}
{"level":"error","msg":"Failed to connect to remote backup service.","time":"2025-10-02T17:30:00+05:30"}
{"level":"fatal","msg":"Critical configuration file 'config.yml' not found.","time":"2025-10-02T17:30:00+05:30"}
```

## Implementation Requirements

### Logger Configuration (`setupLogger` function)

- Set the log formatter to `logrus.JSONFormatter`
- Set the output to `os.Stdout`
- Set the log level based on a provided string (e.g., `"info"`, `"debug"`). If the string is invalid, default to `logrus.InfoLevel`

### Main Logic (`runLogbookOperations` function)

This function should contain at least one log statement for each of the six levels:

- **Debug**: Log verbose details useful for development (e.g., `"Checking system status..."`)
- **Info**: Log informational messages about application progress (e.g., `"Logbook application starting up."`)
- **Warn**: Log potential issues that don't prevent the application from running (e.g., `"Disk space is running low."`)
- **Error**: Log errors the application might recover from (e.g., `"Failed to connect to remote backup service."`)
- **Fatal**: Log a critical error that must terminate the application (e.g., `"Critical configuration file not found."`)
- `Fatal` calls `os.Exit(1)` after logging
- **Panic**: Log a message and then panic. This is for unrecoverable application states

---

## Testing Requirements

Your solution must pass tests that verify:

- The logger is correctly configured (formatter, output)
- Setting a specific log level (e.g., `Warn`) correctly filters out lower-level messages (`Info`, `Debug`)
- Messages are logged in the expected JSON format
- A **Fatal** level log correctly triggers an exit
- A **Panic** level log correctly causes a panic
---
15 changes: 15 additions & 0 deletions packages/logrus/challenge-1-basic-logging-and-levels/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module logrus

go 1.21

require (
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.11.1
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
18 changes: 18 additions & 0 deletions packages/logrus/challenge-1-basic-logging-and-levels/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
98 changes: 98 additions & 0 deletions packages/logrus/challenge-1-basic-logging-and-levels/hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Hints for Challenge 1: Basic Logging & Levels

## Hint 1: Configuring the Logger Output

In the `setupLogger` function, the first step is to tell Logrus where to send the logs. The function receives an `io.Writer` named `out`.

```go
// Tell the global logrus logger to write to the `out` variable.
logrus.SetOutput(out)
```

---

## Hint 2: Setting the JSON Formatter

To make logs structured, you need to set the formatter. Create a new instance of `logrus.JSONFormatter` and pass its address to `logrus.SetFormatter`.

```go
// This tells logrus to format all subsequent logs as JSON.
logrus.SetFormatter(&logrus.JSONFormatter{})
```

---

## Hint 3: Parsing and Setting the Log Level

You need to convert the `level` string (e.g., `"debug"`) into a `logrus.Level` type. The `logrus.ParseLevel` function does this for you. It returns the level and an error if the string is invalid.

```go
// Try to parse the level string.
lvl, err := logrus.ParseLevel(level)

// If the string is not a valid level, `err` will not be nil.
if err != nil {
// In case of an error, we fall back to a sensible default.
logrus.SetLevel(logrus.InfoLevel)
} else {
// If parsing was successful, use the parsed level.
logrus.SetLevel(lvl)
}
```

---

## Hint 4: Logging the First Message

In the `runLogbookOperations` function, you can use the package-level functions like `logrus.Debug()`, `logrus.Info()`, etc., to log messages.

```go
// For the first TODO, use the Debug function.
logrus.Debug("Checking system status...")
```

---

## Hint 5: Logging the Remaining Messages

Follow the same pattern for the other log levels in the specified order.

```go
func runLogbookOperations() {
logrus.Debug("Checking system status...")
logrus.Info("Logbook application starting up")
logrus.Warn("Disk space is running low")
logrus.Error("Failed to connect to remote backup service")
logrus.Fatal("Critical configuration file 'config.yml' not found")
logrus.Panic("Unhandled database connection issue")
}
```

---

## Hint 6: Understanding Fatal and Panic

Remember the special behavior of `Fatal` and `Panic`:

* `logrus.Fatal(...)` will log the message **and then immediately terminate** the program (by calling `os.Exit(1)`). No code after it will run.
* `logrus.Panic(...)` will log the message **and then cause a panic**.

This means in a normal run, you will never see the `Panic` log if the `Fatal` log comes before it. The tests are designed to handle these specific behaviors.

---

## Hint 7: Running and Testing Your Code

Once you've filled in the TODOs, you can run your `main.go` file from the terminal and pass a log level as an argument to see the effect.

```bash
# Run with the default "info" level (no debug messages)
go run .

# Run and show only messages from "warn" level and above
go run . warn

# Run with "debug" level to see all messages
go run . debug
```
---
158 changes: 158 additions & 0 deletions packages/logrus/challenge-1-basic-logging-and-levels/learning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Learning: Basic Logging in Go with Logrus

## **What is Logging?**

Logging is how your application records what’s happening while it runs. It’s your “black box.”
When something fails in production, logs are usually the only way to see what went wrong.

**Why log?**

* **Debugging** – track down issues without a debugger
* **Monitoring** – check health/performance
* **Auditing** – record important events
* **Clarity** – know what your code was doing at a given time

---

## **Go’s Built-in Logging**

Go ships with the `log` package:

```go
import "log"

func main() {
log.Println("Hello from standard log")
}
```

Output looks like:

```
2025/10/02 18:00:00 Hello from standard log
```

It’s nice for basics, but:

* No levels (everything is just a line)
* No JSON/structured output
* Hard to configure

---

## **Logrus: A Better Logger**

[Logrus](https://github.com/sirupsen/logrus) is the most common logging library for Go.
It’s compatible with `log` but adds:

* **Levels**: Debug, Info, Warn, Error, Fatal, Panic
* **Formatters**: Text or JSON
* **Configurable Output**: Console, file, or anything that implements `io.Writer`

---

## **Core Concepts**

### **1. Setup Logger**

```go
import (
"os"
"github.com/sirupsen/logrus"
)

func setupLogger() {
logrus.SetOutput(os.Stdout) // send logs to console
logrus.SetFormatter(&logrus.JSONFormatter{}) // use JSON format
logrus.SetLevel(logrus.InfoLevel) // default: Info and above
}
```

---

### **2. Log Levels**

Levels control which logs are shown.

From lowest → highest severity:

* `Debug` → development details
* `Info` → normal operations
* `Warn` → something’s off but still running
* `Error` → operation failed
* `Fatal` → critical, app exits
* `Panic` → logs + panics

Example:

```go
logrus.Debug("Checking system status...")
logrus.Info("Logbook app starting")
logrus.Warn("Disk space is low")
logrus.Error("Failed to connect to backup service")
logrus.Fatal("Critical config file missing") // exits
logrus.Panic("Database connection issue") // panics
```

---

### **3. Choosing Levels at Runtime**

You can set the level dynamically from input:

```go
func setupLogger(level string) {
logrus.SetOutput(os.Stdout)
logrus.SetFormatter(&logrus.JSONFormatter{})

lvl, err := logrus.ParseLevel(level)
if err != nil {
lvl = logrus.InfoLevel
}
logrus.SetLevel(lvl)
}
```

---

## **Building a Simple Logbook App**

```go
func runLogbookOperations() {
logrus.Debug("Checking system status...")
logrus.Info("Logbook application starting up.")
logrus.Warn("Disk space is running low.")
logrus.Error("Failed to connect to remote backup service.")
logrus.Fatal("Critical configuration file 'config.yml' not found.")
logrus.Panic("Unhandled database connection issue.")
}

func main() {
logLevel := "info"
if len(os.Args) > 1 {
logLevel = os.Args[1]
}

setupLogger(logLevel)
logrus.Infof("Log level set to '%s'", logrus.GetLevel().String())
runLogbookOperations()
}
```

---

## **Best Practices**

1. Pick the right level: `Debug` for dev, `Info` for normal ops, `Error` when something breaks.
2. Default to JSON formatter — easy to parse later.
3. Don’t log secrets (passwords, keys).
4. Keep messages clear: “Failed to connect to DB” > “error 17”.

---

## 🔗 **Resources**

* [Logrus GitHub](https://github.com/sirupsen/logrus)
* [Go by Example – Logging](https://gobyexample.com/logging)

---
Loading