Skip to content
This repository has been archived by the owner on Feb 13, 2023. It is now read-only.

romapres2010/httpserver

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

33 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Новая вСрсия Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°

Π¨Π°Π±Π»ΠΎΠ½ backend сСрвСра Π½Π° Golang - Ρ‡Π°ΡΡ‚ΡŒ 1 (HTTP сСрвСр)

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²Π»Π΅Π½Π½Ρ‹ΠΉ Π½ΠΈΠΆΠ΅ шаблон сСрвСра Π½Π° Golang Π±Ρ‹Π» ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²Π»Π΅Π½ для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π·Π½Π°Π½ΠΈΠΉ Π²Π½ΡƒΡ‚Ρ€ΠΈ нашСй ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹. Основная Ρ†Π΅Π»ΡŒ шаблона, ΠΊΡ€ΠΎΠΌΠ΅ обучСния - это ΡΠ½ΠΈΠ·ΠΈΡ‚ΡŒ врСмя Π½Π° ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π΅Π±ΠΎΠ»ΡŒΡˆΠΈΡ… сСрвСрных Π·Π°Π΄Π°Ρ‡ Π½Π° Go.

Π¨Π°Π±Π»ΠΎΠ½ Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚:

  • ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρƒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² для запуска HTTP сСрвСра Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠΌΠ°Π½Π΄Π½ΡƒΡŽ строку github.com/urfave/cli
  • Настройка ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² сСрвСра Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ» github.com/sasbury/mini
  • Настройка ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² TLS HTTP сСрвСра
  • Настройка Ρ€ΠΎΡƒΡ‚Π΅Ρ€Π° ΠΈ рСгистрация HTTP ΠΈ prof-ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² github.com/gorilla/mux
  • Настройка ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ логирования Π±Π΅Π· остановки сСрвСра github.com/hashicorp/logutils
  • Настройка логирования HTTP Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ° Π±Π΅Π· остановки сСрвСра
  • Настройка логирования ошибок Π² HTTP response Π±Π΅Π· остановки сСрвСра
  • HTTP Basic аутСнтификация
  • MS AD аутСнтификация gopkg.in/korylprince/go-ad-auth.v2
  • JSON Web Token github.com/dgrijalva/jwt-go
  • Запуск сСрвСра с ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ΠΌ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π° Π² ΠΊΠ°Π½Π°Π» ошибок
  • ИспользованиС контСкста для ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠΉ остановки сСрвСра ΠΈ связанных сСрвисов
  • Настройка кастомной ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок github.com/pkg/errors
  • Настройка кастомного логирования
  • Π‘Π±ΠΎΡ€ΠΊΠ° с Π²Π½Π΅Π΄Ρ€Π΅Π½ΠΈΠ΅ΠΌ вСрсии, Π΄Π°Ρ‚Ρ‹ сборки ΠΈ commit

Бсылка Π½Π° Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.

Π’ состав шаблона Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΎ нСсколько HTTP ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ²:

  • POST /echo - трансляция request HTTP ΠΈ body Π² response
  • POST /signin - аутСнтификация ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ JSON Web Token Π² Cookie
  • POST /refresh - ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ ΠΆΠΈΠ·Π½ΠΈ JSON Web Token Π² Cookie
  • POST /httplog - настройка логирования HTTP Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°
  • POST /httperrlog - настройка логирования ошибок Π² HTTP response
  • POST /loglevel - настройка ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ логирования DEBUG, INFO, ERROR

ΠŸΠΎΠ΄Ρ…ΠΎΠ΄ ΠΊ ΡƒΠΏΡ€ΠΎΡ‰Π΅Π½ΠΈΡŽ написания HTTP ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² для этого шаблона описан Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅ Π£ΠΏΡ€ΠΎΡ‰Π°Π΅ΠΌ написаниС HTTP ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² Π½Π° Golang

Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΠ΅ ΡΡ‚Π°Ρ‚ΡŒΠΈ

  1. ΠŸΡ€Π΅Π΄Ρ‹ΡΡ‚ΠΎΡ€ΠΈΡ
  2. ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² сСрвСру
    2.2. Командная строка
    2.3. ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ»
  3. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅, запуск ΠΈ остановка сСрвСра
    3.1. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ daemon ΠΈ сСрвисов
    3.2. Запуск daemon ΠΈ сСрвисов
    3.3. ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ° daemon ΠΈ сСрвисов
  4. ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок
    4.1. ΠšΠ°ΡΡ‚ΠΎΠΌΠ½Π°Ρ структура ошибки
    4.2. Π€ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅Ρ‡Π°Ρ‚ΠΈ ошибки
    4.3. РСгистрация ошибок
    4.4. Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок
  5. Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅
    5.1. ΠšΡƒΠ΄Π° Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ
    5.2. Π€ΠΎΡ€ΠΌΠ°Ρ‚ логирования
    5.3. Как Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ
    5.4. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ HTTP Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°
  6. АутСнтификация
  7. ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ ΠΊΠΎΠ΄Π° ΠΈ сборка
    7.1. ИспользованиС go mod
    7.2. Π‘Π±ΠΎΡ€ΠΊΠ° ΠΊΠΎΠ΄Π°

1. ΠŸΡ€Π΅Π΄Ρ‹ΡΡ‚ΠΎΡ€ΠΈΡ

Π’ Ρ…ΠΎΠ΄Π΅ внСдрСния 1Π‘:ERP, появилась интСрСсная Π·Π°Π΄Π°Ρ‡Π° - интСграция 1Π‘ с шиной IBM MQ. ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹ΠΌΠΈ трСбованиями Π² части взаимодСйствия с IBM MQ Π±Ρ‹Π»ΠΈ:

  • ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡƒΠ»ΠΎΠΌ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ ΠΊ IBM MQ (минимальноС, максимальноС, врСмя простоя)
  • автоматичСскоС ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π½Π° Ρ€Π΅Π·Π΅Ρ€Π²Π½Ρ‹ΠΉ ΡƒΠ·Π΅Π» IBM MQ ΠΏΡ€ΠΈ сбоС основного ΡƒΠ·Π»Π°
  • использованиС Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ€Π΅ΠΆΠΈΠΌΠ° ΠΏΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с IBM MQ (SYNCPOINT)

Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π±Ρ‹Π»ΠΈ Π²Ρ‹Π΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹ трСбования ΠΊ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ XML сообщСний:

  • нормализация (канонизация) сообщСний ΠΏΠΎ стандарту RFC 3076
  • использованиС ΠΌΠ΅Ρ‚ΠΎΠΊ цСлостности для Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ сообщСний ΠΏΠΎ кастомному Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΡƒ HMAC с Ρ…ΡΡˆ-Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ Π“ΠžΠ‘Π’-34.11.94
  • ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΌ кэшСм сСкрСтных ΠΊΠ»ΡŽΡ‡Π΅ΠΉ для вычислСния HMAC

Π‘Ρ‚Π°Π½Π΄Π°Ρ€Ρ‚Π½ΠΎΠ³ΠΎ Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π° Π² 1C ΠΊ IBM MQ Π½Π΅ Π±Ρ‹Π»ΠΎ. Π‘ΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ REST API ΠΊ IBM MQ Π½Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΠ» ΠΏΠΎΠ΄ трСбования.

АдаптСр 1C ΠΊ IBM MQ Π±Ρ‹Π» ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π°Π½ Π½Π° Go с использованиСм ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ IBM MQ. Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° отличаСтся Π½Π΅ΠΏΠ»ΠΎΡ…ΠΎΠΉ ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ, Ρ‡Ρ‚ΠΎ ΠΈ Π½Π΅ ΡƒΠ΄ΠΈΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ½Π° написана Π² Π²ΠΈΠ΄Π΅ "ΠΎΠ±Π΅Ρ€Ρ‚ΠΊΠΈ" Π½Π°Π΄ стандартной C Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΎΠΉ. Π—Π° ΠΏΠΎΠ»Π³ΠΎΠ΄Π° Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π½Π΅ΠΉ Π±Ρ‹Π»ΠΎ зафиксировано всСго 2 Π±Π°Π³Π° с ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΎΠΉ слайсов [].

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²Π»Π΅Π½Π½Ρ‹ΠΉ Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅ шаблон backend сСрвСра, являСтся ΠΎΠ±ΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΠΎΠ³ΠΎ ΠΎΠΏΡ‹Ρ‚Π°.

АрхитСктура Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π° 1Π‘ ΠΊ IBM MQ ΡƒΠΊΡ€ΡƒΠΏΠ½Π΅Π½ΠΎ ΠΏΠΎΠΊΠ°Π·Π°Π½Π° Π½Π° ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ рисункС.

REST_IBMMQ

2. ΠŸΠ΅Ρ€Π΅Π΄Π°Ρ‡Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² сСрвСру

2.1. Командная строка

ВсС Ρ‡ΡƒΠ²ΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅, с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния бСзопасности, ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ сСрвСра ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠΌΠ°Π½Π΄Π½ΡƒΡŽ строку. Для этого ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° github.com/urfave/cli. Бписок основных ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²:

   --httpconfig value, --httpcfg value    HTTP Config file name
   --listenstring value, -l value         Listen string in format <host>:<port>
   --httpuser value, --httpu value        User name for access to HTTP server
   --httppassword value, --httppwd value  User password for access to HTTP server
   --jwtkey value, --jwtk value           JSON web token secret key
   --debug value, -d value                Debug mode: DEBUG, INFO, ERROR
   --logfile value, --log value           Log file name

2.2. ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ»

Для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° github.com/sasbury/mini. Бписок Ρ‚ΠΈΠΏΠΎΠ²Ρ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ², Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ‹Ρ… Π² шаблон:

[HTTP_SERVER]
ReadTimeout = 6000          // HTTP read timeout duration in sec - default 60 sec
WriteTimeout = 6000         // HTTP write timeout duration in sec - default 60 sec
IdleTimeout = 6000          // HTTP idle timeout duration in sec - default 60 sec
MaxHeaderBytes = 262144     // HTTP max header bytes - default 1 MB
MaxBodyBytes = 1048576      // HTTP max body bytes - default 0 - unlimited
UseProfile = false          // use Go profiling
ShutdownTimeout = 30        // service shutdown timeout in sec - default 30 sec

[TLS]
UseTLS = false                  // use SSL
UseHSTS = false                 // use HTTP Strict Transport Security
TLSΠ‘ertFile = certs/server.pem  // TLS Certificate file name
TLSKeyFile = certs/server.key   // TLS Private key file name
TLSMinVersion = VersionTLS10    // TLS min version VersionTLS13, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30
TLSMaxVersion = VersionTLS12    // TLS max version VersionTLS13, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30

[JWT]
UseJWT = false                  // use JSON web token (JWT)
JWTExpiresAt = 20000            // JWT expiry time in seconds - 0 without restriction

[AUTHENTIFICATION]
AuthType = INTERNAL             // Autehtification type NONE | INTERNAL | MSAD
MSADServer = company.com        // MS Active Directory server
MSADPort = 389                  // MS Active Directory Port
MSADBaseDN = OU=, DC=, DC=      // MS Active Directory BaseDN
MSADSecurity = SecurityNone     // MS Active Directory Security: SecurityNone, SecurityTLS, SecurityStartTLS

[LOG]
HTTPLog = false                         // Log HTTP traffic
HTTPLogType = INREQ                     // HTTP trafic log mode INREQ | OUTREQ | INRESP | OUTRESP | BODY
HTTPLogFileName = ./httplog/http%s.log  // HTTP log file
HTTPErrLog = HEADER | BODY              // Log error into HTTP response header and body

3. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅, запуск ΠΈ остановка сСрвСра

На ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ рисункС ΠΏΠΎΠΊΠ°Π·Π°Π½Π° упрощСнная UML Π΄ΠΈΠ°Π³Ρ€Π°ΠΌΠΌΠ° ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ запуска ΠΈ остановки сСрвСра.

http_server_run_stop

Для ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ†ΠΈΠΈ создания, запуска ΠΈ остановки сСрвСра ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ daemon. Π’ Π΅Π³ΠΎ Π·Π°Π΄Π°Ρ‡ΠΈ Π²Ρ…ΠΎΠ΄ΠΈΡ‚:

  • считываниС ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π°
  • настройка ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ сСрвисов
  • созданиС контСкста context.Context
  • созданиС ΠΊΠ°Π½Π°Π»ΠΎΠ² ошибок для ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠΉ связи с сСрвисами
  • созданиС зависимых сСрвисов
  • ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ запуск сСрвисов
  • ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ систСмных ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΉ ΠΈ/ΠΈΠ»ΠΈ ошибок ΠΎΡ‚ сСрвисов
  • коррСктная остановка сСрвисов

3.1. Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ daemon ΠΈ сСрвисов

Π’ ΠΎΠ±Ρ‰Π΅ΠΌ случаС, сСрвисы ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ ΠΈ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°ΡŽΡ‚ΡΡ ΠΏΡ€ΠΈ создании daemon.
Если Π΅ΡΡ‚ΡŒ ошибки ΠΏΡ€ΠΈ создании ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… сСрвисах, Ρ‚ΠΎ daemon Π½Π΅ создаСтся.
Если сСрвис ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ Π² Ρ„ΠΎΠ½Π΅, Ρ‚ΠΎ Π² daemon для Π½Π΅Π³ΠΎ создаСтся ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΊΠ°Π½Π°Π» ошибок:

httpserverErrCh: make(chan error, 1), // ΠΊΠ°Π½Π°Π» ошибок HTTP сСрвСра

ΠŸΡ€ΠΈ созданиС сСрвиса, Π΅ΠΌΡƒ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹:

  • контСкст daemon - ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π² сСрвис ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ
  • ΠΊΠ°Π½Π°Π» ошибок - ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π° ΠΈΠ· сСрвиса Π² daemon ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎ ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎΠΉ ошибкС
  • структуру с ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹ΠΌΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ сСрвиса

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ Π·Π°Π΄Π°Ρ‡ ΠΏΡ€ΠΈ создании сСрвисов:

  • Для сСрвиса Ρ€Π°Π±ΠΎΡ‚Ρ‹ с IBM MQ:
    • ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹
    • создаСтся контСкст сСрвиса
    • дСлаСтся тСстовоС ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ кластСру IBM MQ, опрСдСляСтся ΠΊΠ°ΠΊΠΎΠΉ ΠΈΠ· ΡƒΠ·Π»ΠΎΠ² кластСра являСтся Ρ€Π°Π±ΠΎΡ‡ΠΈΠΌ, Π° ΠΊΠ°ΠΊΠΎΠΉ находится Π² Ρ€Π΅Π·Π΅Ρ€Π²Π΅
    • открываСтся ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΠ» ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ ΠΊ IBM MQ
  • Для сСрвиса Ρ€Π°Π±ΠΎΡ‚Ρ‹ с PostgreSQL:
    • ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹
    • создаСтся контСкст сСрвиса
    • дСлаСтся тСстовоС ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅
    • парсятся, ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½Ρ‹Π΅, SQL ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹
  • Для сСрвиса ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ JSON Π² BoltDB:
    • ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹
    • создаСтся контСкст сСрвиса
    • открываСтся Ρ„Π°ΠΉΠ» BoltDB Π½Π° запись
    • происходит считываниС Π·Π°ΠΊΠ΅ΡˆΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Ρ… ΠΊΠ»ΡŽΡ‡Π΅ΠΉ ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ Π²Π°Π»ΠΈΠ΄Π½ΠΎΡΡ‚ΡŒ кэша (Π΄Π°Π½Π½Ρ‹Π΅ ΠΌΠΎΠ³Π»ΠΈ ΠΏΠΎΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ Π² Π‘Π” PostgreSQL). Π­Ρ‚Π° опСрация ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ пСрСнСсСна Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ„ΠΎΠ½ΠΎΠ²Ρ‹ΠΉ процСсс, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡΠΎΠΊΡ€Π°Ρ‚ΠΈΡ‚ΡŒ врСмя старта сСрвСра (Π² Ρ…ΠΎΠ΄Π΅ тСста Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ BoltDB Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ 150 Π“Π±Π°ΠΉΡ‚ ΡƒΡ…ΠΎΠ΄ΠΈΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 2 ΠΌΠΈΠ½ΡƒΡ‚Ρ‹ Π² 64 ΠΏΠΎΡ‚ΠΎΠΊΠ° ΠΏΡ€ΠΈ условии, Ρ‡Ρ‚ΠΎ BoltDB Ρ€Π°Π·ΠΌΠ΅Ρ‰Π΅Π½Π° Π½Π° NVMe дискС со срСдним Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΎΡ‚ΠΊΠ»ΠΈΠΊΠ° 0.03 ms).
  • Для HTTP сСрвСра:
    • ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‚ΡΡ Π²Ρ…ΠΎΠ΄Π½Ρ‹Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹
    • создаСтся контСкст сСрвиса
    • создаСтся ΠΈ настраиваСтся http.server
    • создаСтся TCP листСнСр
    • Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°ΡŽΡ‚ΡΡ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ TLS
    • создаСтся Ρ€ΠΎΡƒΡ‚Π΅Ρ€
    • Ρ€Π΅Π³ΠΈΡΡ‚Ρ€ΠΈΡ€ΡƒΡŽΡ‚ΡΡ HTTP ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ
    • Ρ€Π΅Π³ΠΈΡΡ‚Ρ€ΠΈΡ€ΡƒΡŽΡ‚ΡΡ pprof ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ

3.2. Запуск daemon ΠΈ сСрвисов

Запуск daemon Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² скоординированном запускС сСрвисов.
Π’Π°ΠΊ ΠΊΠ°ΠΊ всС сСрвисы ΡƒΠΆΠ΅ Π±Ρ‹Π»ΠΈ созданы Ρ€Π°Π½Π΅Π΅, Ρ‚ΠΎ запуск сСрвиса - это, ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ, Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ листСнСра ΠΈΠ»ΠΈ установлСниС Ρ„Π»Π°Π³Π°, Ρ€Π°Π·Ρ€Π΅ΡˆΠ°ΡŽΡ‰Π΅Π³ΠΎ Π½Π°Ρ‡Π°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ.
Для запуска сСрвисов Π² Ρ„ΠΎΠ½Π΅, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ анонимная функция с Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ΠΎΠΌ Π² ΠΊΠ°Π½Π°Π» ошибок. ΠŸΡ€ΠΈΠΌΠ΅Ρ€, запуска HTTP сСрвСра

go func() { httpserverErrCh <- d.httpserver.Run() }()

ПослС запуска сСрвисов, daemon подписываСтся Π½Π° основныС систСмныС прСрывания ΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄ΠΈΡ‚ Π² Ρ€Π΅ΠΆΠΈΠΌ оТидания сигналов ΠΈΠ»ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚Π° Π² ΠΊΠ°Π½Π°Π»Ρ‹ ошибок ΠΎΡ‚ сСрвисов.
ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ daemon ошибки ΠΎΡ‚ сСрвиса, ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ ΠΈΠ· сСрвисов Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ. Π—Π΄Π΅ΡΡŒ Π»ΠΎΠ³ΠΈΠΊΠ° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ сущСствСнно ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ, ΠΎΡ‚ ΠΏΠΎΠ»Π½ΠΎΠΉ остановки (ΠΊΠ°ΠΊ Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π½ΠΈΠΆΠ΅) Π΄ΠΎ пСрСзапуска сбойного сСрвиса.
НапримСр, Ссли основной сСрвСр IBM MQ становится нСдоступСн, Ρ‚ΠΎ daemon ΠΏΡ€ΠΎΠ±ΡƒΠ΅Ρ‚ ΠΏΠ΅Ρ€Π΅ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ сСрвис Π½Π° Ρ€Π΅Π·Π΅Ρ€Π²Π½ΠΎΠΌ сСрвСрС IBM MQ ΠΈ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ.

syscalCh := make(chan os.Signal, 1) // ΠΊΠ°Π½Π°Π» систСмных ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠΉ
signal.Notify(syscalCh, syscall.SIGINT, syscall.SIGTERM)

// ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌ прСрывания ΠΈΠ»ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ Π² ΠΊΠ°Π½Π°Π» ошибок
select {
case s := <-syscalCh: // систСмноС ΠΏΡ€Π΅Ρ€Ρ‹Π²Π°Π½ΠΈΠ΅
    mylog.PrintfInfoMsg("Exiting, got signal", s)
    d.Shutdown() // останавливаСм daemon
    return nil
case err := <-d.httpserverErrCh: // Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ ΠΎΡ‚ HTTP сСрвСра Π² ΠΊΠ°Π½Π°Π» ошибок
    mylog.PrintfErrorInfo(err) // Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ
    d.Shutdown() // останавливаСм daemon
    return err
}

Π’ запуск сСрвисов, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΡ… Π² Ρ„ΠΎΠ½Π΅, добавляСтся анонимная функция восстановлСния послС ΠΏΠ°Π½ΠΈΠΊΠΈ (ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π½ΠΈΠΆΠ΅). ΠŸΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ ΠΏΠ°Π½ΠΈΠΊΠΈ, ошибка возвращаСтся Π² ΠΊΠ°Π½Π°Π» ошибок для увСдомлСния daemon.

func (s *Server) Run() error {
    defer func() {
        var myerr error
        r := recover()
        if r != nil {
            msg := "Recover from panic"
            switch t := r.(type) {
            case string:
                myerr = myerror.New("8888", msg, t)
            case error:
                myerr = myerror.WithCause("8888", msg, t)
            default:
                myerr = myerror.New("8888", msg)
            }
            mylog.PrintfErrorInfo(myerr) // Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ
            s.errCh <- myerr             // ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ ΠΎΡˆΠΈΠ±ΠΊΡƒ Π² ΠΊΠ°Π½Π°Π» для увСдомлСния daemon
        }
    }()

    // Запуск сСрвСра
}

3.3. ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ° daemon ΠΈ сСрвисов

ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ° daemon Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² скоординированной остановкС сСрвисов ΠΈ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠ³ΠΎ контСкста.

ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΊΠ° сСрвисов, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΡ… Π² Ρ„ΠΎΠ½Π΅, осущСствляСтся ΠΏΠΎ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌΡƒ ΡΡ†Π΅Π½Π°Ρ€ΠΈΡŽ:

  • устанавливаСтся Ρ‚Π°ΠΉΠΌΠ΅Ρ€ оТидания ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ остановки (ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ ShutdownTimeout Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΌ Ρ„Π°ΠΉΠ»Π΅)
  • закрываСтся контСкст сСрвиса
  • Π² Π·Π°Π΄Π°Ρ‡Ρƒ всСх сСрвисов Π²Ρ…ΠΎΠ΄ΠΈΡ‚ коррСктная остановка Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΏΡ€ΠΈ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ ΠΈΡ… контСкста. На ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ сСрвиса IBM MQ это:
    • ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ… сообщСний
    • Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠ΅ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ
    • Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π² ΠΏΡƒΠ»
    • Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚Ρ‹Ρ… ΠΎΡ‡Π΅Ρ€Π΅Π΄Π΅ΠΉ
    • Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΡƒΠ»Π° Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ ΠΊ IBM MQ
  • послС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ остановки сСрвис отправляСт ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ Π² ΠΊΠ°Π½Π°Π» stopCh

Для ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎΠΉ остановки сСрвисов ΠΏΡ€ΠΈ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ контСкста, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄:

  • Π² ΠΊΠΎΡ€Π½Π΅Π²Ρ‹Ρ… Ρ†ΠΈΠΊΠ»Π°Ρ… добавляСтся ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° состояния контСкста. Если контСкст Π·Π°ΠΊΡ€Ρ‹Ρ‚, Ρ‚ΠΎ ΠΎΡ‡Π΅Ρ€Π΅Π΄Π½ΡƒΡŽ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΡŽ Π½Π΅ Π½Π°Ρ‡ΠΈΠ½Π°Ρ‚ΡŒ ΠΈ ΠΎΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡ‚ΡŒ рСсурсы
  • Π² ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°Ρ…, Π² бСзопасных мСстах, добавляСтся ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° состояниС контСкста, Ссли контСкст Π·Π°ΠΊΡ€Ρ‹Ρ‚, Ρ‚ΠΎ Π½Π΅ Π½Π°Ρ‡ΠΈΠ½Π°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ
for {
    select {
    case <-ctx.Done(): // ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ сигнал закрытия контСкста

        // ΠžΡΠ²ΠΎΠ±ΠΎΠ΄ΠΈΡ‚ΡŒ рСсурсы

        s.stopCh <- struct{}{} // ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ
        return
    default:
        // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΎΡ‡Π΅Ρ€Π΅Π΄Π½ΠΎΠΉ ΠΈΡ‚Π΅Ρ€Π°Ρ†ΠΈΠΈ
    }
}

Для остановки HTTP сСрвСра использовался нСсколько Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄:

// создаСм Π½ΠΎΠ²Ρ‹ΠΉ контСкст с ΠΎΡ‚ΠΌΠ΅Π½ΠΎΠΉ ΠΈ отсрочкой ShutdownTimeout
cancelCtx, cancel := context.WithTimeout(s.ctx, time.Duration(s.cfg.ShutdownTimeout*int(time.Second)))
defer cancel()

// ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌ закрытия Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π² Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠΈ ShutdownTimeout
if err := s.httpServer.Shutdown(cancelCtx); err != nil {
    return err
}

s.httpService.Shutdown() // ΠžΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ слуТСбныС сСрвисы

// ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎΠ± ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠΈ HTTP сСрвСра
s.stopCh <- struct{}{}

4. ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок

4.1. ΠšΠ°ΡΡ‚ΠΎΠΌΠ½Π°Ρ структура ошибки

Один ΠΈΠ· Π½Π°ΠΈΠ±ΠΎΠ»Π΅Π΅ ΡƒΠ΄Π°Ρ‡Π½Ρ‹Ρ… ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибок github.com/pkg/errors.
ΠŸΠ΅Ρ€Π²ΠΎΠ½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎ использовал Π΅Π³ΠΎ, Π½ΠΎ со Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ стало Π½Π΅ Ρ…Π²Π°Ρ‚Π°Ρ‚ΡŒ структурности ошибки, поэтому ΠΏΠ΅Ρ€Π΅ΡˆΠ΅Π» Π½Π° простой кастомный ΠΏΠ°ΠΊΠ΅Ρ‚.

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° для хранСния ошибки:

type Error struct {
    ID       uint64 // ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π½ΠΎΠΌΠ΅Ρ€ ошибки
    Code     string // код ошибки
    Msg      string // тСкст ошибки
    Caller   string // Ρ„Π°ΠΉΠ», строка ΠΈ Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² мСстС рСгистрации ошибки
    Args     string // строка Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ²
    CauseErr error  // ошибка - ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°
    CauseMsg string // тСкст ошибки - ΠΏΡ€ΠΈΡ‡ΠΈΠ½Ρ‹
    Trace    string // стСк Π²Ρ‹Π·ΠΎΠ²Π° Π² мСстС рСгистрации ошибки
}

МнС ΡƒΠ΄ΠΎΠ±Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Ρ‚ΠΈΠΏΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΌΠΈ ошибками, поэтому ΠΊΠΎΠ΄ ошибки Π²Ρ‹Π΄Π΅Π»Π΅Π½ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΌ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠΌ. НапримСр, Π² Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π΅ 1Π‘ ΠΊ IBM MQ, использовался простой 4 ΡΠΈΠΌΠ²ΠΎΠ»ΡŒΠ½Ρ‹ΠΉ числовой ΠΊΠΎΠ΄. НапримСр, ошибки Π½Π°Ρ‡ΠΈΠ½Π°ΡŽΡ‰ΠΈΠ΅ΡΡ с "8Ρ…Ρ…Ρ…" ΠΎΡ‚Π½ΠΎΡΠΈΠ»ΠΈΡΡŒ ΠΊ HTTP, с "7Ρ…Ρ…Ρ…" - ΠΊ IBM MQ.

Caller - Ρ„Π°ΠΉΠ», строка ΠΈ Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° Π² мСстС рСгистрации ошибки. Π£Π΄ΠΎΠ±Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ, Ссли Π½Π΅Ρ‚ нСобходимости Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΏΠΎΠ»Π½Ρ‹ΠΉ стСк. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ‹Π²ΠΎΠ΄Π°:

httpserver.go:[209] - (*Server).Run()

Caller вычисляСтся Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠ΅ΠΉ

func caller(depth int) string {
    pc := make([]uintptr, 15)
    n := runtime.Callers(depth+1, pc)
    frame, _ := runtime.CallersFrames(pc[:n]).Next()
    idxFile := strings.LastIndexByte(frame.File, '/')
    idx := strings.LastIndexByte(frame.Function, '/')
    idxName := strings.IndexByte(frame.Function[idx+1:], '.') + idx + 1

    return frame.File[idxFile+1:] + ":[" + strconv.Itoa(frame.Line) + "] - " + frame.Function[idxName+1:] + "()"
}

Args - ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Π°Ρ строка Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊ ΡΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΡŽ ΠΏΡ€ΠΈ рСгистрации ошибки. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для Ρ†Π΅Π»Π΅ΠΉ ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ.

CauseErr ΠΈ CauseMsg - исходная ошибка ΠΈ сообщСниС. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ, Ссли ΠΎΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Π΅ΠΌ Ρ‡ΡƒΠΆΡƒΡŽ ΠΎΡˆΠΈΠ±ΠΊΡƒ Π² свою структуру.

Trace - стандартный трСйс стСка. Для Π΅Π³ΠΎ получСния использовал нСсколько своСобразный ΠΏΠΎΠ΄Ρ…ΠΎΠ΄. ΠŸΡ€ΠΈ рСгистрации ошибки создавал Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΏΡƒΡΡ‚ΡƒΡŽ ΠΎΡˆΠΈΠ±ΠΊΡƒ ΠΈΠ· ΠΏΠ°ΠΊΠ΅Ρ‚Π° github.com/pkg/errors ΠΈ ΠΏΠ΅Ρ‡Π°Ρ‚Π°Π» Π΅Π΅ с ΠΊΠ»ΡŽΡ‡ΠΎΠΌ '%+v'. Π’ этом Ρ€Π΅ΠΆΠΈΠΌΠ΅ ΠΎΠ½Π° Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ стСк.

fmt.Sprintf("'%+v'", pkgerr.New(""))

4.2. Π€ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠ΅Ρ‡Π°Ρ‚ΠΈ ошибки

Для соотвСтствия интСрфСйсу Error, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π²Ρ‹Π²ΠΎΠ΄ Π² сокращСнном Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅.

func (e *Error) Error() string {
    mes := fmt.Sprintf("ID=[%v], code=[%s], mes=[%s]", e.ID, e.Code, e.Msg)
    if e.Args != "" {
        mes = fmt.Sprintf("%s, args=[%s]", mes, e.Args)
    }
    if e.CauseMsg != "" {
        mes = fmt.Sprintf("%s, causemes=[%s]", mes, e.CauseMsg)
    }
    return mes
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ‹Π²ΠΎΠ΄Π° Π² сокращСнном Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅

ID=[1], code=[8004], mes=[Error message], args=['arg1', 'arg2', 'arg3']

Для Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½Π½ΠΎΠ³ΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ Π²Ρ‹Π²ΠΎΠ΄Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ ΠΊΠ»ΡŽΡ‡ΠΈ

// %s    print the error code, message, arguments, and cause message.
// %v    in addition to %s, print caller
// %+v   extended format. Each Frame of the error's StackTrace will be printed in detail.
func (e *Error) Format(s fmt.State, verb rune) {
    switch verb {
    case 'v':
        fmt.Fprint(s, e.Error())
        fmt.Fprintf(s, ", caller=[%s]", e.Caller)
        if s.Flag('+') {
            fmt.Fprintf(s, ", trace=%s", e.Trace)
            return
        }
    case 's':
        fmt.Fprint(s, e.Error())
    case 'q':
        fmt.Fprint(s, e.Error())
    }
}

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π²Ρ‹Π²ΠΎΠ΄Π° с ΠΊΠ»ΡŽΡ‡ΠΎΠΌ '%+v'

ID=[1], code=[8004], mes=[Error message], args=['arg1', 'arg2', 'arg3'], caller=[handler_echo.go:[31] - (*Service).EchoHandler.func1()], trace='
github.com/romapres2010/httpserver/error.New
        D:/golang/src/github.com/romapres2010/httpserver/error/error.go:72
github.com/romapres2010/httpserver/httpserver/httpservice.(*Service).EchoHandler.func1
        D:/golang/src/github.com/romapres2010/httpserver/httpserver/httpservice/handler_echo.go:31
        ...

ΠŸΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½Π½Ρ‹ΠΉ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π²Ρ‹Π²ΠΎΠ΄Π° Π½Π΅ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ соотвСтствуСт ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρƒ структурированного логирования. Π­Ρ‚ΠΎ сдСлано ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎ для Π±ΠΎΠ»Π΅Π΅ ΡƒΠ΄ΠΎΠ±Π½ΠΎΠ³ΠΎ чтСния Π»ΠΎΠ³Π° Π² Ρ…ΠΎΠ΄Π΅ ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ. Если Π½ΡƒΠΆΠ΅Π½ Π±ΠΎΠ»Π΅ строгий Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ - достаточно ΠΏΠΎΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС ΠΌΠ΅Ρ‚ΠΎΠ΄ Format.

4.3. РСгистрация ошибок

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Π΄Π²Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π° для рСгистрации ошибок:

  • Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠΉ ошибки
New(code string, msg string, args ...interface{}) error
  • ΠžΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Π½ΠΈΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅ΠΉ ошибки
WithCause(code string, msg string, causeErr error, args ...interface{}) error

Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ ΠΌΠΎΠΆΠ½ΠΎ Π»ΠΈΠ±ΠΎ Π²ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ Π² сообщСниС ошибки, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ Π² args ...interface{}.

ВсС ошибки ΠΎΡ‚ сторонних ΠΈ стандартных ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ² ΠΎΠ±ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°ΡŽΡ‚ΡΡ Π² ΠΊΠ°ΡΡ‚ΠΎΠΌΠ½ΡƒΡŽ ΠΎΡˆΠΈΠ±ΠΊΡƒ Π² мСстС возникновСния. Π˜ΡΡ…ΠΎΠ΄Π½Π°Ρ ошибка вкладываСтся Π²Π½ΡƒΡ‚Ρ€ΡŒ кастомной, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

myerr = myerror.WithCause("8001", "Failed to read HTTP body: reqID", err, reqID)

4.4. Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ошибок

Использовался ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄:

  • Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ возникновСния, ошибка логируСтся Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ INFO Π±Π΅Π· trace. Π’ этот ΠΌΠΎΠΌΠ΅Π½Ρ‚, ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ, Π½Π΅ извСстно, являСтся Π»ΠΈ это ошибкой, ΠΈΠ»ΠΈ ΠΎΠ½Π° Π±ΡƒΠ΄Π΅Ρ‚ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π° Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ Π²Ρ‹ΡˆΠ΅
  • ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π΅ ошибок Π½Π° ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ Π²Π²Π΅Ρ€Ρ… ΠΎΠ½Π° ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎ Π½Π΅ оборачиваСтся Π² WithCause() ΠΈ Π½Π΅ логируСтся
  • Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ошибки логируСтся Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ INFO. Ошибка пСрСстаСт Π±Ρ‹Ρ‚ΡŒ ошибкой.
  • Ссли ошибка дошла Π½Π΅ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½ΠΎΠΉ Π΄ΠΎ самого Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ уровня, Π·Π½Π°Ρ‡ΠΈΡ‚ это Π΄Π΅ΠΉΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ошибка ΠΈ ΠΎΠ½Π° логируСтся Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ ERROR с максимальной Π΄Π΅Ρ‚Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ trace.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ логирования ΠΏΡ€ΠΈ ошибкС чтСния Ρ‚Π΅Π»Π° HTTP запроса:

requestBuf, err := ioutil.ReadAll(r.Body)
if err != nil {
    myerr = myerror.WithCause("8001", "Failed to read HTTP body: reqID", err, reqID)
    mylog.PrintfErrorInfo(myerr) // стандартноС сокращСнноС Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ошибки
    s.processError(myerr, w, http.StatusInternalServerError, reqID) // Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½Π½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ошибки Π² HTTP response
    return myerr
}

ΠœΠ΅Ρ‚ΠΎΠ΄ΠΎΠΌ processError Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ошибка ΠΌΠΎΠΆΠ΅Ρ‚ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ HTTP ΠΎΡ‚Π²Π΅Ρ‚Π° ΠΈ/ΠΈΠ»ΠΈ Ρ‚Π΅Π»ΠΎ ΠΎΡ‚Π²Π΅Ρ‚Π°. ΠΠ΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ Ρ‚Π°ΠΊΠΎΠ³ΠΎ логирования настраиваСтся Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΌ Ρ„Π°ΠΉΠ»Π΅ сСрвСра - ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ HTTPErrLog, ΠΈΠ»ΠΈ динамичСски Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ POST Π½Π° /httperrlog.

POST /httperrlog HTTP/1.1
HTTP-Err-Log: HEADER | BODY

ΠŸΡ€ΠΈ записи многострочного тСкста Π² HTTP header Π½ΡƒΠΆΠ½ΠΎ Π½Π΅ Π·Π°Π±Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ всС ΡƒΠΏΡ€Π°Π²Π»ΡΡŽΡ‰ΠΈΠ΅ символы

// carriage return (CR, ASCII 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII 0x0)
headerReplacer := strings.NewReplacer("\x0a", " ", "\x0d", " ", "\x00", " ")

w.Header().Set("Err-Trace", headerReplacer.Replace(myerr.Trace))

5. Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅

Для управлСния уровнями логирования использовался ΠΏΠ°ΠΊΠ΅Ρ‚ github.com/hashicorp/logutils.
Из Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΠΎΠ²Π°Π½Π½ΠΎΠ³ΠΎ списка ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ логирования RFC 5424 β€” The Syslog Protocol, Π² шаблонС оставил Ρ‚ΠΎΠ»ΡŒΠΊΠΎ:

  • debug β€” подробная информация для ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ
  • info β€” ΠΏΠΎΠ»Π΅Π·Π½Ρ‹Π΅ события, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, запуск/останов сСрвиса
  • error β€” ошибки исполнСния, Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‰ΠΈΠ΅ Π²ΠΌΠ΅ΡˆΠ°Ρ‚Π΅Π»ΡŒΡΡ‚Π²Π°

ΠšΠ°ΡΡ‚ΠΎΠΌΠ½Ρ‹ΠΉ ΠΏΠ°ΠΊΠ΅Ρ‚ логирования получился ΠΊΡ€Π°ΠΉΠ½Π΅ простым - всСго 100 строк

Бсылка Π½Π° ΠΏΠΎΠ»Π΅Π·Π½ΡƒΡŽ ΡΡ‚Π°Ρ‚ΡŒΡŽ ΠΎΡ‚ Dave Cheney Let’s talk about logging.
Если ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½Π½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ логирования покаТСтся слишком простым - Ρ‚ΠΎ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΡŽ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π² сторону A simple logging interface for Go.

5.1. ΠšΡƒΠ΄Π° Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ

ВсС Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈΠ΄Π΅Ρ‚ Ρ‡Π΅Ρ€Π΅Π· стандартный ΠΏΠ°ΠΊΠ΅Ρ‚ "log". Но для Π½Π΅Π³ΠΎ подмСняСтся Π²Ρ‹Π²ΠΎΠ΄ Π½Π° кастомный Π»ΠΎΠ³Π΅Ρ€ github.com/hashicorp/logutils.

// logFilter represent a custom logger seting
var logFilter = &logutils.LevelFilter{
    Levels:   []logutils.LogLevel{"DEBUG", "INFO", "ERROR"},
    MinLevel: logutils.LogLevel("INFO"), // initial setting
    Writer:   os.Stderr,                 // initial setting
}

// InitLogger init custom logger
func InitLogger(wrt io.Writer) {
    logFilter.Writer = wrt   // custom logger
    log.SetOutput(logFilter) // set std logger to our custom
}

ΠŸΡ€ΠΈ нСобходимости, ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π² Ρ„Π°ΠΉΠ», для этого Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ main создаСтся Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» ΠΈ устанавливаСтся MultiWriter

// настраиваСм ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Ρ„Π°ΠΉΠ»
if logFileFlag != "" {
    // добавляСм Π² имя Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π° Π΄Π°Ρ‚Ρƒ ΠΈ врСмя
    logFileFlag = strings.Replace(logFileFlag, "%s", time.Now().Format("2006_01_02_150405"), 1)

    // ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» Π½Π° запись Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ APPEND
    logFile, err := os.OpenFile(logFileFlag, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        myerr := myerror.WithCause("6020", "Error open log file: Filename", err, logFileFlag)
        mylog.PrintfErrorMsg(fmt.Sprintf("%+v", myerr))
        return myerr
    }
    if logFile != nil {
        defer logFile.Close()
    }

    wrt := io.MultiWriter(os.Stderr, logFile) // ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ пишСм Π² os.Stderr ΠΈ Ρ„Π°ΠΉΠ»

    mylog.InitLogger(wrt) // пСрСопрСдСляСм стандартный Π»ΠΎΠ³Π΅Ρ€ Π½Π° кастомный
} else {
    mylog.InitLogger(os.Stderr)
}

5.2. Π€ΠΎΡ€ΠΌΠ°Ρ‚ логирования

Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ INFO ΠΈ DEBUG сдСлано Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ Π±ΠΎΠ»Π΅Π΅ друТСствСнным Ρ‡Π΅ΠΌ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ошибок. ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π½ΠΈΠΆΠ΅

2020/03/10 17:56:39 [INFO] - httpserver.go:[61] - New() - Creating new HTTP server
2020/03/10 17:56:39 [INFO] - httpserver.go:[141] - New() - Created new TCP listener: network = 'tcp', address ['127.0.0.1:3000']
2020/03/10 17:56:39 [INFO] - httpserver.go:[155] - New() - Handler is registered: Path, Method ['/echo', 'POST']
2020/03/10 17:56:39 [INFO] - httpserver.go:[155] - New() - Handler is registered: Path, Method ['/signin', 'POST']
2020/03/10 17:56:39 [INFO] - httpserver.go:[155] - New() - Handler is registered: Path, Method ['/refresh', 'POST']
2020/03/10 17:56:39 [INFO] - httpserver.go:[179] - New() - HTTP server is created
2020/03/10 17:56:39 [INFO] - daemon.go:[121] - New() - New daemon is created
2020/03/10 17:56:39 [INFO] - daemon.go:[128] - (*Daemon).Run() - Starting daemon
2020/03/10 17:56:39 [INFO] - daemon.go:[133] - (*Daemon).Run() - Daemon is running. For exit <CTRL-c>
2020/03/10 17:56:39 [INFO] - httpserver.go:[209] - (*Server).Run() - Starting HTTP server
2020/03/10 17:57:22 [INFO] - daemon.go:[142] - (*Daemon).Run() - Exiting, got signal ['interrupt']
2020/03/10 17:57:22 [INFO] - daemon.go:[155] - (*Daemon).Shutdown() - Shutting down daemon
2020/03/10 17:57:22 [INFO] - httpserver.go:[215] - (*Server).Shutdown() - Waiting for shutdown HTTP Server: sec ['30']
2020/03/10 17:57:22 [INFO] - httpserver.go:[231] - (*Server).Shutdown() - HTTP Server shutdown successfuly
2020/03/10 17:57:22 [INFO] - daemon.go:[167] - (*Daemon).Shutdown() - Daemon is shutdown
2020/03/10 17:57:22 [INFO] - main.go:[163] - main.func1() - Server is shutdown

Π’ Ρ€Π΅ΠΆΠΈΠΌΠ΅ ERROR ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π²Ρ‹Π²ΠΎΠ΄Π° ΠΈΠ· Ρ€Π°Π·Π΄Π΅Π»Π° Π²Ρ‹ΡˆΠ΅ ΠΏΡ€ΠΎ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ошибок.

5.3. Как Π»ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ

Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ уровня логирования сдСланы ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ:

func PrintfInfoMsg(mes string, args ...interface{}) {
    printfMsg("[INFO]", 0, mes, args...)
}
func PrintfDebugMsg(mes string, args ...interface{}) {
    printfMsg("[DEBUG]", 0, mes, args...)
}
func PrintfErrorMsg(mes string, args ...interface{}) {
    printfMsg("[ERROR]", 0, mes, args...)
}

Для мСня это ΡƒΠ΄ΠΎΠ±Π½Π΅Π΅, Ρ‡Π΅ΠΌ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠ΅ логирования ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΉ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ. Π•Ρ‰Π΅ ΠΎΠ΄ΠΈΠ½ плюс - Π»Π΅Π³ΠΊΠΎ Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ΠΎ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π²Ρ‹Π²ΠΎΠ΄ DEBUG сообщСний ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π΅ Π² ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΈΠ².

Π§Ρ‚ΠΎΠ±Ρ‹ ΡΠ½ΠΈΠ·ΠΈΡ‚ΡŒ Π·Π°Ρ‚Ρ€Π°Ρ‚Ρ‹ Π½Π° Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ INFO ΠΈ DEBUG, Π»ΡƒΡ‡ΡˆΠ΅ ΠΎΡ‚ΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ форматирования строк Π² Ρ‚ΠΎΡ‡ΠΊΠ΅ Π²Ρ‹Π·ΠΎΠ²Π°. ВмСсто

mylog.PrintfInfoMsg(fmt.Sprintf("Create new TCP listener network='tcp', address='%s'", serverCfg.ListenSpec))

Π»ΡƒΡ‡ΡˆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ

mylog.PrintfInfoMsg("Created new TCP listener: network = 'tcp', address", cfg.ListenSpec)

ВСсты ΠΏΠΎΠΊΠ°Π·Π°Π»ΠΈ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈ использовании Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°, Π½Π°ΠΊΠ»Π°Π΄Π½Ρ‹Π΅ расходы Π½Π° Π²Ρ‹Π·ΠΎΠ²Ρ‹ логирования DEBUG Π² Ρ€Π΅ΠΆΠΈΠΌΠ΅ INFO Π½Π΅ прСвысили 1%.
Π’ этом Π΅ΡΡ‚ΡŒ сущСствСнный ΠΏΠΎΠ»ΠΎΠΆΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ - ΠΌΠΎΠΆΠ½ΠΎ динамичСски ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ с INFO Π½Π° DEBUG Π±Π΅Π· остановки сСрвСра Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ POST Π½Π° /loglevel.

POST /loglevel HTTP/1.1
Log-Level-Filter: DEBUG | ERROR | INFO

5.4. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ HTTP Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ°

Для Π°Π½Π°Π»ΠΈΠ·Π° слоТных ситуаций, ΠΏΠΎΠ»Π΅Π·Π½ΠΎ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ логирования HTTP Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ° проходящСго Ρ‡Π΅Ρ€Π΅Π· сСрвСр.
Π’ ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ httplog собраны ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для логирования HTTP header ΠΈ body:

  • входящСго запроса
  • исходящСго ΠΎΡ‚Π²Π΅Ρ‚Π°
  • исходящСго запроса
  • входящСго ΠΎΡ‚Π²Π΅Ρ‚Π°

Настройка Ρ‚ΠΈΠΏΠ° логирования ΠΈ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π° ΠΌΠΎΠΆΠ΅Ρ‚ Π·Π°Π΄Π°Π²Π°Ρ‚ΡŒΡΡ Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΌ Ρ„Π°ΠΉΠ»Π΅ - ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ HttpLog ΠΈ HTTPLogType, ΠΈΠ»ΠΈ динамичСски Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ POST Π½Π° /httplog.

POST /httplog HTTP/1.1
Http-Log: TRUE
Http-Log-Type: INREQ | OUTREQ | INRESP | OUTRESP | BODY

6. АутСнтификация

Π’ шаблон Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹ Π΄Π²Π° Π±Π°Π·ΠΎΠ²Ρ‹Ρ… способа Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ: HTTP Basic Authentication ΠΈΠ»ΠΈ MS AD Authentication.
Для MS AD Authentication ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° gopkg.in/korylprince/go-ad-auth.v2.
На ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π°, сСрвСра ΠΌΠΎΠΆΠ½ΠΎ Π·Π°Π΄Π°Ρ‚ΡŒ Ρ‚ΠΈΠΏ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ сСрвСру MS AD.

[AUTHENTIFICATION]
AuthType = INTERNAL | MSAD | NONE
MSADServer = company.com
MSADPort = 389
MSADBaseDN = OU=company, DC=dc, DC=corp
MSADSecurity = SecurityNone

ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ ΠΈ ΠΏΠ°Ρ€ΠΎΠ»ΡŒ для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ HTTP Basic Authentication пСрСдаСтся Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠΌΠ°Π½Π΄ΡƒΡŽ строку ΠΏΡ€ΠΈ стартС сСрвСра.

ИспользованиС JSON Web Token (JWT) задаСтся Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Π° сСрвСра. ВрСмя ΠΆΠΈΠ·Π½ΠΈ Ρ‚ΠΎΠΊΠ΅Π½Π° задаСтся ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠΌ JWTExpiresAt (JWTExpiresAt=0 - врСмя ΠΆΠΈΠ·Π½ΠΈ Π½Π΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΎ). Π‘Π΅ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ JWT пСрСдаСтся Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠΌΠ°Π½Π΄ΡƒΡŽ строку ΠΏΡ€ΠΈ стартС сСрвСра.

[JWT]
UseJWT = true
JWTExpiresAt = 20000

Для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с JWT ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° github.com/dgrijalva/jwt-go.
Вся ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° JWT: созданиС, ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°, Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ cookie собрано Π² нСбольшой кастомный ΠΏΠ°ΠΊΠ΅Ρ‚ jwt

Π›ΠΎΠ³ΠΈΠΊΠ° использования JWT ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ:

  • Ссли JWT Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½, Ρ‚ΠΎ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ входящСм запросС Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ HTTP Basic Authentication ΠΈΠ»ΠΈ MS AD Authentication
  • Ссли JWT Π²ΠΊΠ»ΡŽΡ‡Π΅Π½, Ρ‚ΠΎ всС запросы Π±Π»ΠΎΠΊΠΈΡ€ΡƒΡŽΡ‚ΡΡ (StatusUnauthorized), ΠΏΠΎΠΊΠ° Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½Π° аутСнтификация ΠΈ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ JWT
  • аутСнтификация ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ JWT выполняСтся Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ POST Π½Π° /signin
    • ΠΏΡ€ΠΈ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ формируСтся JSON Web Token Claim (Π² Claim Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ), устанавливаСт врСмя Π΅Π³ΠΎ ΠΆΠΈΠ·Π½ΠΈ
    • ΠΈΠ· Claims формируСтся JSON Web Token, подписываСтся Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ HS256 вмСстС с сСкрСтным ΠΊΠ»ΡŽΡ‡ΠΎΠΌ
    • сформированный Token помСщаСтся Π² http Cookie "token". Для Cookie устанавливаСтся Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½ΠΎΠ΅ Token врСмя ΠΆΠΈΠ·Π½ΠΈ
  • ΠΏΡ€ΠΈ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΡ… запросах, ΠΈΠ· http Cookie извлСкаСтся JWT ΠΈ провСряСтся. Если врСмя ΠΆΠΈΠ·Π½ΠΈ Π·Π°ΠΊΠΎΠ½Ρ‡ΠΈΠ»ΠΎΡΡŒ, Ρ‚ΠΎ StatusUnauthorized
  • ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ JWT выполняСтся Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ POST Π½Π° /refresh
  • logout Π½Π΅ прСдусмотрСн

7. ΠžΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΡ ΠΊΠΎΠ΄Π° ΠΈ сборка

7.1. ИспользованиС go mod

Для ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ ΠΏΠ΅Ρ€Π΅ΡˆΠ΅Π» с golang/dep/cmd/dep Π½Π° go mod, Π½ΠΎ для хранСния Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ ΠΏΠΎ ΠΏΡ€Π΅ΠΆΠ½Π΅ΠΌΡƒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΠ°ΠΏΠΊΠ° vendor Π² ΠΊΠΎΡ€Π½Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. Π­Ρ‚ΠΎ позволяСт Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ "ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹Π΅" вСрсии Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ Π² своСм Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. ОбновлСниС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ выполняСтся ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ.

go get -u ./../...
go mod vendor

ΠŸΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π΅ Π½Π° go mod столкнулся с ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ, Ρ‡Ρ‚ΠΎ Ρ‡Π°ΡΡ‚ΡŒ сторонних Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‚ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Ρƒ с модулями ΠΈ загруТаСтся Π½Π΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Π°Ρ вСрсия. Для Ρ‚Π°ΠΊΠΈΡ… Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ Π½ΡƒΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ Π² ΠΊΠΎΠ½Ρ†Π΅ Π½ΠΎΠΌΠ΅Ρ€ Π½ΡƒΠΆΠ½ΠΎΠ³ΠΎ commit, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

go get github.com/ibm-messaging/mq-golang/ibmmq@19b946c

Для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ компилятор Π±Ρ€Π°Π» Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΈΠ· ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π° vendor, Π½ΡƒΠΆΠ½ΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΎΠΏΡ†ΠΈΡŽ

go build -v -mod vendor

7.2. Π‘Π±ΠΎΡ€ΠΊΠ° ΠΊΠΎΠ΄Π°

Для сборки ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ простой make Ρ„Π°ΠΉΠ» с нСсколькими Ρ€Π΅ΠΆΠΈΠΌΠ°ΠΌΠΈ (взят Π³Π΄Π΅-Ρ‚ΠΎ Π½Π° посторах ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚Π°):

  • rebuild - полная пСрСсборка
  • build - ΠΈΠ½ΠΊΡ€Π΅ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Π°Ρ сборка
  • check - ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΊΠΎΠ΄Π° с использованиСм github.com/golangci/golangci-lint

ΠŸΡ€ΠΈ сборС Π² исполняСмый Ρ„Π°ΠΉΠ» внСдряСтся вСрсия, Π΄Π°Ρ‚Π° сборки ΠΈ commit. Π­Ρ‚Π° информация выводится Π² Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» - вСсьма ΠΏΠΎΠ»Π΅Π·Π½ΠΎ для Ρ€Π°Π·Π±ΠΎΡ€Π° ошибок.
Для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ‚Π°ΠΊΠΎΠ³ΠΎ внСдрСния, Π² main добавляСм ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅

var (
    version   = "0.0.2" // Π½ΠΎΠΌΠ΅Ρ€ вСрсии, задаСтся Ρ€ΡƒΠΊΠ°ΠΌΠΈ
    commit    = "unset" // Π½ΠΎΠΌΠ΅Ρ€ commit
    buildTime = "unset" // Π΄Π°Ρ‚Π° ΠΈ врСмя сборки
)

ΠŸΡ€ΠΈ сборС Π² make Ρ„Π°ΠΉΠ»Π΅, Π·Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Π΅ΠΌ git ΠΎ commit

COMMIT?=$(shell git rev-parse --short HEAD)
BUILD_TIME?=$(shell date -u '+%Y-%m-%d_%H:%M:%S')

ΠŸΡ€ΠΈ сборкС привязываСм эти значСния ΠΊ Ρ€Π°Π½Π΅Π΅ ΠΎΠ±ΡŠΡΠ²Π»Π΅Π½Π½Ρ‹ΠΌ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌ (опция -ldflags "-X"), Π·Π°ΠΎΠ΄Π½ΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½ΠΊΡƒ, ΠΏΠΎΠ΄ ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ собираСм ΠΈ мСсто хранСния Π±ΠΈΠ½Π°Ρ€Π½ΠΈΠΊΠ°

GOOS=${GOOS} go build -v -a -mod vendor \
-ldflags "-X main.commit=${COMMIT} -X main.buildTime=${BUILD_TIME}" \
-o bin/${GOOS}/${APP} 

Π’ main.main Π² ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ Π·Π°ΠΏΠΈΡΡ‹Π²Π°ΡŽ ΠΈΡ‚ΠΎΠ³ΠΎΠ²ΡƒΡŽ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ вСрсии, commit ΠΈ Π΄Π°Ρ‚Π΅ сборки

app.Version = fmt.Sprintf("%s, commit '%s', build time '%s'", version, commit, buildTime)

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published