Решения, вышеперечисленные с использованием классов с int-константами, не имеют безопасности типа. То есть вы могли бы изобретать новые значения, фактически не определенные в классе. Кроме того, не представляется возможным, например, написать метод, принимающий один из этих классов в качестве входных данных.
Вам нужно было бы написать
public void DoSomethingMeaningFull(int consumeValue) ...
. Однако существует решение класса на основе старые дни Java, когда не было доступных перечислений. Это обеспечивает почти перечислимое поведение. Единственное предостережение состоит в том, что эти константы не могут использоваться в инструкции switch.
public class MyBaseEnum
{
public static readonly MyBaseEnum A = new MyBaseEnum( 1 );
public static readonly MyBaseEnum B = new MyBaseEnum( 2 );
public static readonly MyBaseEnum C = new MyBaseEnum( 3 );
public int InternalValue { get; protected set; }
protected MyBaseEnum( int internalValue )
{
this.InternalValue = internalValue;
}
}
public class MyEnum : MyBaseEnum
{
public static readonly MyEnum D = new MyEnum( 4 );
public static readonly MyEnum E = new MyEnum( 5 );
protected MyEnum( int internalValue ) : base( internalValue )
{
// Nothing
}
}
[TestMethod]
public void EnumTest()
{
this.DoSomethingMeaningful( MyEnum.A );
}
private void DoSomethingMeaningful( MyBaseEnum enumValue )
{
// ...
if( enumValue == MyEnum.A ) { /* ... */ }
else if (enumValue == MyEnum.B) { /* ... */ }
// ...
}
Вы можете перехватить ведение журнала, предоставив io.Writer
- log.SetOutput . Там вы можете просто проверить, является ли записываемая строка тем, что вам нужно отследить и записать трассировку стека делегированному завернутому модулю записи.
Вот пример игровой площадки: https://play.golang.org/p/2bClt2JBuFs
Обратите внимание, что получение текущей трассировки стека не является бесплатным, оно вызывает остановку и может резко замедлить ваше приложение. Если у вас есть приложение, которое создает большой объем журналов, я бы рекомендовал ограничить количество отслеживаемых данных (например, трассировать только первое совпадение или N раз в секунду / минуту)
package main
import (
"fmt"
"log"
"os"
"regexp"
"runtime/debug"
)
func main() {
log.SetOutput(NewLogInterceptor(LogInterceptionCheck{Pattern: ".*Some.*", Description: "with 'Some' substring"}))
f1()
log.Println("message that is not traced")
}
func f1() {
log.Println("Some message")
}
type LogInterceptor struct {
target *os.File
checks []LogInterceptionCheck
}
type LogInterceptionCheck struct {
regexp *regexp.Regexp
Pattern string
Description string
}
func NewLogInterceptor(checks ...LogInterceptionCheck) *LogInterceptor {
for i := 0; i < len(checks); i++ {
each := checks[i]
compiled, e := regexp.Compile(each.Pattern)
if e != nil {
log.Fatalf("cannot compile regexpt [%s]: %s", each, e)
}
checks[i].regexp = compiled
}
return &LogInterceptor{os.Stderr, checks}
}
func (interceptor *LogInterceptor) Write(p []byte) (n int, err error) {
i, err := interceptor.target.Write(p)
// use loop because it is faster and generates less garbage compared to for-range loop
for i := 0; i < len(interceptor.checks); i++ {
check := interceptor.checks[i]
if check.regexp.Match(p) {
_, e := fmt.Fprintf(interceptor.target, ">>>> Printing stacktrace [%s]\n", check.Description)
if e != nil {
log.Fatalf("cannot write: %s", e)
}
_, e = interceptor.target.Write(debug.Stack())
if e != nil {
log.Fatalf("cannot write stacktrace: %s", e)
}
break
}
}
return i, err
}