Python logging 예제

|

Pythong logging 모듈 사용하기

Python에 기본으로 내장된 logging 모듈에 대한 사용법입니다. 로그 레벨에 따라 debug, info, warning, error, critical의 5가지 등급이 있으며 이 중에서 warningwarn이라는 메소드도 같이 존재합니다. warndeprecated 되었기 때문에, 가급적 warn은 사용하지 말고 warning을 사용하도록 주의합시다.

간단한 예제

import logging


def main():
    logging.debug('debug')
    logging.info('info')
    logging.warning('warning')
    logging.error('error')
    logging.critical('critical')


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG,
                        format='[%(asctime)s] [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)')
    main()


Colored logging 사용하기

여기에 색상을 입히는 코드를 적용해봅니다.

color_palette.py

# Reset
RESET = '\033[0m'

# Regular Colors
BLACK = '\033[30m'
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'

# Bold
BOLD_BLACK = '\033[1;30m'
BOLD_RED = '\033[1;31m'
BOLD_GREEN = '\033[1;32m'
BOLD_YELLOW = '\033[1;33m'
BOLD_BLUE = '\033[1;34m'
BOLD_MAGENTA = '\033[1;35m'
BOLD_CYAN = '\033[1;36m'
BOLD_WHITE = '\033[1;37m'

# Underline
UNDERLINE_BLACK = '\033[4;30m'
UNDERLINE_RED = '\033[4;31m'
UNDERLINE_GREEN = '\033[4;32m'
UNDERLINE_YELLOW = '\033[4;33m'
UNDERLINE_BLUE = '\033[4;34m'
UNDERLINE_MAGENTA = '\033[4;35m'
UNDERLINE_CYAN = '\033[4;36m'
UNDERLINE_WHITE = '\033[4;37m'

# High Intensity
INTENSITY_BLACK = '\033[0;90m'
INTENSITY_RED = '\033[0;91m'
INTENSITY_GREEN = '\033[0;92m'
INTENSITY_YELLOW = '\033[0;93m'
INTENSITY_BLUE = '\033[0;94m'
INTENSITY_MAGENTA = '\033[0;95m'
INTENSITY_CYAN = '\033[0;96m'
INTENSITY_WHITE = '\033[0;97m'

colored_log_handler.py

import logging

from color_palette import RESET, GREEN, WHITE, YELLOW, MAGENTA, RED


class ColoredLogHandler(logging.StreamHandler):
    def __init__(self):
        super().__init__()
        self.setLevel(logging.DEBUG)
        self.setFormatter(self.__LogFormatter())

    class __LogFormatter(logging.Formatter):
        __FORMAT_DEBUG = '[%(asctime)s] [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)'
        __FORMAT_INFO = '[%(asctime)s] [%(levelname)s] %(message)s'
        __FORMAT_WARNING = '[%(asctime)s] [%(levelname)s] %(message)s'
        __FORMAT_ERROR = '[%(asctime)s] [%(levelname)s] %(message)s'
        __FORMAT_CRITICAL = '[%(asctime)s] [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)'

        FORMATS = {
            logging.DEBUG: GREEN + __FORMAT_DEBUG + RESET,
            logging.INFO: WHITE + __FORMAT_INFO + RESET,
            logging.WARNING: YELLOW + __FORMAT_WARNING + RESET,
            logging.ERROR: MAGENTA + __FORMAT_ERROR + RESET,
            logging.CRITICAL: RED + __FORMAT_CRITICAL + RESET
        }

        def format(self, record):
            log_fmt = self.FORMATS.get(record.levelno)
            formatter = logging.Formatter(log_fmt)
            return formatter.format(record)

main.py

import logging
from colored_log_handler import ColoredLogHandler


def main():
    logging.debug('debug')
    logging.info('info')
    logging.warning('warning')
    logging.error('error')
    logging.critical('critical')


if __name__ == '__main__':
    logging.basicConfig(level="DEBUG", handlers=[ColoredLogHandler()])

    main()

ZSH Plug-in 소개

|

ZSH Plug-in 소개

zsh 셀에서 키워드를 입력한 다음 화살표 위 방향 키로 입력된 키워드가 포함된 과거 실행 명령어를 보여주는 유용한 플러그인입니다.

 git clone https://github.com/zsh-users/zsh-history-substring-search ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-history-substring-search

syntax-highlighting

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting

~/.zshrc 설정

plugins=(
  git
  alias-tips
  zsh-autosuggestions
  zsh-syntax-highlighting
)

Flutter Stream 예제

|

simple_timer.dart

import 'dart:async';

class SampleTimer {
  int seconds = 0;
  bool isRunning = false;

  late Timer timer;

  void start(int seconds) {
    this.seconds = seconds;
    isRunning = true;
  }

  void stop() {
    isRunning = false;
  }

  Stream<int> stream() async* {
    yield* Stream.periodic(const Duration(seconds: 1), (int a) {
      if (isRunning) {
        seconds = seconds - 1;
        if (seconds <= 0) {
          isRunning = false;
        }
      }

      return seconds;
    });
  }
}

timer_page.dart

import 'package:flutter/material.dart';
import 'package:stream_sample/timer/sample_timer.dart';

class TimerPage extends StatelessWidget {
  TimerPage({Key? key}) : super(key: key);

  final timer = SampleTimer();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            const Padding(padding: EdgeInsets.all(8)),
            Expanded(
              child: MaterialButton(
                child: const Text('Start'),
                color: Colors.amber,
                onPressed: () => timer.start(100),
              ),
            ),
            const Padding(padding: EdgeInsets.all(8)),
            Expanded(
              child: MaterialButton(
                child: const Text('Stop'),
                color: Colors.amber,
                onPressed: () => timer.stop(),
              ),
            ),
            const Padding(padding: EdgeInsets.all(8)),
          ],
        ),
        StreamBuilder(
          stream: timer.stream(),
          builder: (context, snapshot) {
            return Text(
              snapshot.data.toString(),
              style: const TextStyle(
                fontSize: 30,
              ),
            );
          },
        )
      ],
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:stream_sample/timer_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('snowdeer\'s timer sample'),
        ),
        body: TimerPage(),
      ),
    );
  }
}

C++ Google Test 샘플

|

Google Test

Google Test 샘플 코드입니다.

CMakeLists.txt

FetchContent 명령어를 이용해서 Google Test 라이브러리를 연동합니다.

cmake_minimum_required(VERSION 3.21)
project(gtest_sample)

set(CMAKE_CXX_STANDARD 17)


include(FetchContent)
FetchContent_Declare(googletest
        GIT_REPOSITORY https://github.com/google/googletest.git
        GIT_TAG release-1.11.0)

FetchContent_MakeAvailable(googletest)
enable_testing()

add_executable(gtest_sample Calculator.cpp CalculatorTests.cpp)

target_link_libraries(
        gtest_sample
        gtest_main
)

Calculator.hpp

#ifndef GTEST_SAMPLE__CALCULATOR_H_
#define GTEST_SAMPLE__CALCULATOR_H_

class Calculator {
 public:
  Calculator();

 public:
  int add(int x, int y);
  int sub(int x, int y);
};


#endif //GTEST_SAMPLE__CALCULATOR_H_

Calculator.cpp

#include "Calculator.hpp"
Calculator::Calculator() {

}
int Calculator::add(int x, int y) {
  return x + y;
}
int Calculator::sub(int x, int y) {
  return x - y;
}

CalculatorTests.cpp

#include <gtest/gtest.h>
#include "Calculator.h"

TEST(Calculator_Add_Test, test_name) {
  Calculator c;
  EXPECT_EQ(8, c.add(3, 5));
}

TEST(Calculator_Sub_Test, test_name) {
  Calculator c;
  EXPECT_EQ(7, c.sub(12, 5));
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

실행결과

/Users/snowdeer/Workspace/cpp/gtest/cmake-build-debug/gtest_sample
[==========] Running 2 tests from 2 test suites.
[----------] Global test environment set-up.
[----------] 1 test from Calculator_Add_Test
[ RUN      ] Calculator_Add_Test.test_name
[       OK ] Calculator_Add_Test.test_name (0 ms)
[----------] 1 test from Calculator_Add_Test (0 ms total)

[----------] 1 test from Calculator_Sub_Test
[ RUN      ] Calculator_Sub_Test.test_name
[       OK ] Calculator_Sub_Test.test_name (0 ms)
[----------] 1 test from Calculator_Sub_Test (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 2 test suites ran. (0 ms total)
[  PASSED  ] 2 tests.

Process finished with exit code 0

Nlohmann Json 라이브러리 샘플

|

nlohmann json

여기에서 코드 확인할 수 있으며, 샘플이나 설명도 자세하게 되어 있습니다. 그 외에도 JsonCppRapidJSON 등의 라이브러리들도 사용해보았지만, 사용 편의성이 가장 우수한게 nlohmann json이었던 것 같습니다.

nlohmann json의 장점은 대략 다음과 같습니다.

  • Modern C++ 연산자를 지원. 마치 Python에서의 JSON 사용과 비슷한 느낌을 제공
  • 쉬운 사용. 헤더 파일 1개(nlohmann/json.hpp)만 include 해서 사용 가능
  • 높은 Coverage의 검증 완료

예제 파일

#include <iostream>
#include <string>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

const auto JSON_INDENT = 2;
const std::string strJson = R"(
    {
      "id" : 123456,
      "res" : 111.222,
      "name" : "Example Json",
      "desc" : "Hello, SnowDeer",
      "data" : [
        {
          "id" : 101,
          "name" : "snowdeer"
        },
        {
          "id" : 202,
          "name" : "ice-rabbit"
        }
      ],
      "info" : {
        "notebook" : "macbook m1 pro",
        "address" : "Seoul"
      }
    }
  )";

void createJsonObjectTest() {
  std::cout << "### Create JSON Object ###" << std::endl;

  json obj;
  obj["id"] = 0;
  obj["data"]["name"] = "snowdeer";
  obj["data"]["age"] = 45;
  obj["data"]["address"] = "Seoul";

  std::cout << obj.dump(JSON_INDENT) << std::endl;
}

void createJsonArrayTest() {
  std::cout << "### Create JSON Array ###" << std::endl;

  json objs;
  objs.push_back("snowdeer");
  objs.push_back("ice-rabbit");
  objs.push_back("fire-bat");

  std::cout << objs.dump(JSON_INDENT) << std::endl;
}

void createMixedJsonObjectTest() {
  std::cout << "### Create Mixed JSON Object ###" << std::endl;

  json obj1 = {{"id", 1},
               {"name", "snowdeer"},
               {"age", 45}};

  json obj2 = {{"id", 2},
               {"name", "ice-rabbit"},
               {"age", 32}};

  json obj3 = {{"id", 3},
               {"name", "fire-bat"},
               {"age", 28}};

  json objs;
  objs.push_back(obj1);
  objs.push_back(obj2);
  objs.push_back(obj3);

  std::cout << objs.dump(JSON_INDENT) << std::endl;
}

void parseJsonTest() {
  std::cout << "### parseJson Test ###" << std::endl;

  auto j = json::parse(strJson);
  std::cout << j.dump(JSON_INDENT) << std::endl;
  std::cout << j["data"].dump(JSON_INDENT) << std::endl;
  std::cout << j["datxxx"].dump(JSON_INDENT) << std::endl;      // 잘못된 Key에 대해서는 null
//  std::cout << j["data"]["name"].dump(JSON_INDENT) << std::endl;    // 배열을 이런 식으로 접근하면 Exception
  std::cout << j["data"][0]["name"].dump(JSON_INDENT) << std::endl;
}

std::string getTypeOfValue(json value) {
  if (value.is_array()) return "array";
  if (value.is_boolean()) return "boolean";
  if (value.is_null()) return "null";
  if (value.is_number_integer()) return "integer";
  if (value.is_number_float()) return "double";
  if (value.is_string()) return "string";
  if (value.is_object()) return "object";

  return "Unknown";
}

void getKeyValueListTest() {
  std::cout << "### Key Value Test ###" << std::endl << std::endl;;

  auto j = json::parse(strJson);
  for (json::iterator it = j.begin(); it != j.end(); ++it) {
    std::cout << "Key : \"" << it.key() << "\"" << std::endl;
    std::cout << "Type : " << getTypeOfValue(it.value()) << std::endl;
    std::cout << "Value : " << it.value() << std::endl;
    std::cout << std::endl;
  }
}

void recursive(json j, int space) {
  std::string indent = "";
  for (auto i = 0; i < space; i++) {
    indent = indent + " ";
  }
  for (json::iterator it = j.begin(); it != j.end(); ++it) {
    std::cout << indent << "Key : \"" << it.key() << "\"" << std::endl;

    if (it.value().is_array()) {
      std::cout << indent << "[" << std::endl;
      for (auto item : it.value()) {
        recursive(item, space + 2);
      }
      std::cout << indent << "]" << std::endl;
    }
    if (it.value().is_object()) {
      std::cout << indent << "{" << std::endl;
      recursive(j[it.key()], space + 2);
      std::cout << indent << "}" << std::endl;
    }

  }
}

void recursiveParseJsonTest() {
  std::cout << "### Recursive parse Json Test ###" << std::endl;

  auto j = json::parse(strJson);
  recursive(j, 0);

}

int main() {
  std::cout << "Hello, World!" << std::endl;

//  createJsonObjectTest();
//  createJsonArrayTest();
//  createMixedJsonObjectTest();

//  parseJsonTest();
//  getKeyValueListTest();
  recursiveParseJsonTest();

  return 0;
}