문제

을 읽은 후"당신의 좋은 제한 순환 복잡성?"나는 나의 많은 동료들과 함께 짜증이 새로운 품질보증 정책에 우리의 프로젝트:더 이상 10 순환 복잡성 당 기능이다.

의:10 개 이하'만약에','다','시험','을 잡아'와는 다른 코드는 워크플로 분기에 문입니다.오른쪽.로 설명서'당신은 당신 테스트 방법 개?',이러한 정책은 많은 좋은 부작용이 나타납니다.

그러나:의 시작 부분에서 우리의(200 명의 사람들-7 년간 긴)프로젝트,우리가 행복하게 로깅(없이,우리는 쉽지 않게 위임하여 어떤 종류의'측면을 지향 프로그래밍'에 대 한 접근 로그의 경우)이 될 수 있습니다.

myLogger.info("A String");
myLogger.fine("A more complicated String");
...

을 때 첫 번째 버전의 우리의 시스템이,우리가 경험하는 거대한 메모리에 문제가 되지 않기 때문에 로깅(었다 한 시점에서 꺼지),그러나 때문에 로그인 매개변수 (문자열)는 항상 계산,다음 전달되는'정보()'또는'좋()'기능을 발견하는 수준의 기록이'에서',그리고 어떤 기록을 복용했다!

그래서 QA 돌아왔을 촉구하고 우리의 프로그래머지 조건부 로그.다.

if(myLogger.isLoggable(Level.INFO) { myLogger.info("A String");
if(myLogger.isLoggable(Level.FINE) { myLogger.fine("A more complicated String");
...

하지만 이제는'할 수 있지 않은 수동'10 순환 복잡성 수준당 기능을 제한,그들이 변론하는 다양한 로그에 넣어 그들은 그들의 기능은 느낌으로 부담이기 때문에,각 경우"(isLoggable())"으로 계산됩+1 순환 복잡성!

그래서 함수의 경우는 8 경'','다'등 중 하나에 단단히 결합되는지 쉽게 공유할 수 없는 알고리즘,그리고 3 중요한 로그 행동...그들이 그 위반을 제한하더라도 조건부 로그하지 않을 수 있습할 품의 부분의 복잡도는 기능을...

어떻게 당신은 주소로 이 상황입니까?
본의 몇 가지 흥미로운 코딩 진화(으로 인해 하는'분쟁')내에서 프로젝트,하지만 난 그냥을 얻고 싶은 당신의 생각은 처음이다.


감사에 대한 모든 답변이 있습니다.
내가 주장하는 문제는'형식과 관련이지만,'인수 평가'관련(평가할 수 있는 매우 비용이 많이 드는 하기 전에,메소드를 호출하는 것이 아무것도 하지 않)
그래서 때 쓴 위에"문자열",실제로 의미하기 위해 사용됩(),와 함께하기 위해 사용됩()문자열을 반환,그리고 전화하여 복잡한 방법의 수집 및 컴퓨팅의 모든 종류 로그 데이터를 표시할 logger...지(따라서,문제고 의무 을 사용하여 조건부 로그,따라서 실제 문제의 인공 증가의'순환 복잡성'...)

나는 이제' 기능'포인트는 고급의 일부를(감사합니다 John).
참고:빠른 테스트에서 java6 쇼 나 varargs 기능 지 평가하기 전에 인수 호출되고,그래서 그것을 적용 할 수 없습니다를 위한 함수 호출하지만,'Log 리트리버 개체'(또는'기능 wrapper')를 toString()경우에만 호출됩니다 필요합니다.그것을 얻었다.

나는 지금은 게시된 내에 경험이 주제입니다.
나는 그것을 떠날 때까지 거기에 다음 주 화요일에 투표에 대한 다음 중 하나를 선택한의 답변이 있습니다.
다시,당신의 제안:)

도움이 되었습니까?

해결책

파이썬에서 당신 통과하는 형식으로 값을 매개 변수를 로깅 기능이다.형식 문자열에만 적용된 경우 로깅을 사용할 수 있습니다.가의 오버헤드를 함수 호출,그러나 그의 소문자에 비해 서식을 지정합니다.

log.info ("a = %s, b = %s", a, b)

당신이 무언가를 할 수 있 다음과 같이 이에 대한 모든 언어로 앞 인수(C/C++,C#,Java,etc.).


이지 못한 경우 인수하기 어려운 검색만을 위해 형식을 지정할 때에 그들을 문자열이 비싸다.예를 들어,당신의 코드가 이미 있는 번호 목록에 그것은,당신은 수 있습 로그인하려는 목록을 위하여 디버깅할 수 있습니다.실행 mylist.toString() 시간이 걸릴 것입니다하지 않으며 결과가 될 것입니다.그래서 당신은 패스 mylist 으로 매개 변수를 로깅 기능,그리고 그것을 처리하는 문자열 형식입니다.는 방법으로,포맷에만 수행될 경우 필요합니다.


이후로는 영업 이익의 질문이 구체적으로 언급하고 있 Java,여기에는 방법은 위를 사용할 수 있습니다:

내가 주장하는 문제는'형식과 관련이지만,'인수 평가'관련(평가할 수 있는 매우 비용이 많이 드는 하기 전에,메소드를 호출하는 것이 아무것도 하지 않)

트릭은이있는 개체를 수행하지 않습니다 비싼 계산할 때까지 절대적으로 필요합니다.이것은 쉬운 언어로 다음과 같 Smalltalk 또는 파이썬을 지원하는 람다와 폐쇄하지만,아직도 할 수 있는 자바에서의 비트와 함께 상상력이다.

말이 있는 기능 get_everything().그것을 검색체는 모든 개체에서의 데이터베이스로 목록입니다.당신이 원하지 않을 호출하는 경우 이 결과는 폐기됩니다.그래서 사용하는 대신화하는 기능에 직접 정의 내부 클래스 라 LazyGetEverything:

public class MainClass {
    private class LazyGetEverything { 
        @Override
        public String toString() { 
            return getEverything().toString(); 
        }
    }

    private Object getEverything() {
        /* returns what you want to .toString() in the inner class */
    }

    public void logEverything() {
        log.info(new LazyGetEverything());
    }
}

이 코드에서 호출 getEverything() 포장도록되지 않은 실제로 실행될 때까지의 필요합니다.로깅 기능이 실행 toString() 에 그것의 매개 변수는 경우에만 디버깅이 사용됩니다.는 방법은,당신의 코드를 겪을 것이만의 오버헤드를 함수 호출 대체 getEverything() 전화입니다.

다른 팁

현재 프레임워크를 로깅,문제는 논

현재 프레임워크를 로깅과 같은 slf4j 또는 log4j2 필요하지 않은 경비 계산서 대부분의 경우에 있습니다.그들이 사용한 매개 변수가 있는 log 문서는 이벤트를 기록할 수 있습 무조건적으로,하지만 메시지 포맷에만 발생하는 경우 이벤트가 활성화됩니다.메시지 건축이 필요에 따라 수행하여 기록보다는 선제 응용 프로그램입니다.

를 사용해야 하는 경우에는 고대 로깅,라이브러리를 읽을 수 있습을 자세한 배경과 방법을 개조하기 위하여 오래된 라이브러리와 매개 변수가 있는 메시지입니다.

는 guard 문 정말 추가 복잡?

제외한 고려한 로깅 경비에서 문을 순환 복잡성을 계산이 됩니다.

그것을 주장 할 수있다,그들의 예측 가능한 형태,조건부 로그 확인이 정말 하지 않에 기여한 복잡성의 코드입니다.

유연 메트릭을 만들 수 있습 그렇지 않으면 좋은 프로그래머요.주의!

가정 도구에 대한 복잡성을 계산할 수 없습에 맞게 그 정도로 다음과 같은 방법을 제공할 수 있습니다.

에 대한 필요 조건부 로그

나는 가정의 가 문을 도입했기 때문에 당신이 이와 같은 코드:

private static final Logger log = Logger.getLogger(MyClass.class);

Connection connect(Widget w, Dongle d, Dongle alt) 
  throws ConnectionException
{
  log.debug("Attempting connection of dongle " + d + " to widget " + w);
  Connection c;
  try {
    c = w.connect(d);
  } catch(ConnectionException ex) {
    log.warn("Connection failed; attempting alternate dongle " + d, ex);
    c = w.connect(alt);
  }
  log.debug("Connection succeeded: " + c);
  return c;
}

Java,각 로그의 문을 새로 생성 StringBuilder, 고를 호출합 toString() 는 방법에는 각 개체에 연결 문자열입니다.이 toString() 방법은 창조하기 위하여 확률이 높 StringBuilder 의 인스턴스를 자신을 호출 toString() 방법은 자신의 회원,그리고에,전에 잠재적으로 대체 그래프입니다.(전 Java5,그것은 더 비싸고,이후 StringBuffer 사용되었으며,모든 작업은 동기화됩니다.)

이 될 수 있습니다 비교적 비용이 많이 드는 경우에 특히 로그에 문은 크게-실행되는 코드의 경로입니다.며,서면 위와 같이 그렇게 비싸 메시지 포맷이 발생하는 경우에도로거가 바 폐기 결과이기 때문에 로그 수준은 너무 높습니다.

에 이르게 도입의 가 문의 양식

  if (log.isDebugEnabled())
    log.debug("Attempting connection of dongle " + d + " to widget " + w);

이드는,평가의 인수 dw 과 문자열 연결이 수행된 경우에만 필요합니다.

는 솔루션을 위한 간단하고,효율적으 로깅

그러나,경우 로거(또는 래퍼는 당신은 주위에 쓰기 선택 로깅 포장)소요 포맷터와 인수에 대한 포맷터 메시지를 건설 할 때까지 지연 될 수있는 그것은 특정 사용하는 동안,제 guard 문과 그들의 순환 복잡성.

public final class FormatLogger
{

  private final Logger log;

  public FormatLogger(Logger log)
  {
    this.log = log;
  }

  public void debug(String formatter, Object... args)
  {
    log(Level.DEBUG, formatter, args);
  }

  … &c. for info, warn; also add overloads to log an exception …

  public void log(Level level, String formatter, Object... args)
  {
    if (log.isEnabled(level)) {
      /* 
       * Only now is the message constructed, and each "arg"
       * evaluated by having its toString() method invoked.
       */
      log.log(level, String.format(formatter, args));
    }
  }

}

class MyClass 
{

  private static final FormatLogger log = 
     new FormatLogger(Logger.getLogger(MyClass.class));

  Connection connect(Widget w, Dongle d, Dongle alt) 
    throws ConnectionException
  {
    log.debug("Attempting connection of dongle %s to widget %s.", d, w);
    Connection c;
    try {
      c = w.connect(d);
    } catch(ConnectionException ex) {
      log.warn("Connection failed; attempting alternate dongle %s.", d);
      c = w.connect(alt);
    }
    log.debug("Connection succeeded: %s", c);
    return c;
  }

}

지금 중식 toString() 전화과 그들의 버퍼가 할당이 발생할 지 않는 한 그들이 필요!이를 효과적으로 제거 성능을 명중하는 led 가드의 문이 있습니다.하나의 작은 벌금,Java 에서는 것이 자동 복싱의 모든 기본 형식 인수를 통과하도록합니다.

코드 하 로깅은 틀림없이 깨끗하 어느 때보다,이후 수선 연결 문자열은 사라졌다.수도 깨끗하는 경우 형식 문자열을 사용할 수 있는 표면화(사용 ResourceBundle),수도원에서 유지 보수 또는 지역화의 소프트웨어입니다.

더욱 향상

또한,Java,a MessageFormat 객체에 사용될 수 있는 장소의"형식" String, 을 제공하는,추가 기능을 선택과 같은 형식으로 처리 숫자를 더 깔끔하게 보관할 수 있습니다.또 다른 대안이 될 것입을 구현하 자체 포맷 기능을 호출하는 일부 인터페이스에 대해 정의하는"평가용"보다는,기본 toString() 방법입니다.

의 언어로 지원하는 람다 식 또는 코드 블록으로 매개변수에 대해 하나의 솔루션이를 제공하는 것 다만 그를 로깅 방법입니다.수는 평가를 구성 그리고 필요한 경우 실제로 전화/실행하여 제공되 lambda/수 있습니다.하지 않았지만 그것은 아직도.

이론적으로 이것이 가능합니다.내가 좋아하지 않기에 그것을 사용으로 인해 생산하는 성능 문제가 나와 함께 기대하는 사용 lamdas/코드 블록에 대한 기록합니다.

하지만 항상:의심하는 경우,테스트 및 측정에 미치는 영향을 cpu 부하다.

해주신 모든 분들께 감사 드립니다.너희들은 바위:)

지금 나의 피드백을 진단 기능을 지원하지 않으로 당신:

프로젝트 (로에서의'하나의 프로그램을 배포하고 실행에 자신의 단일 프로덕션 플랫폼'),난 당신이 갈 수 있는 모든 기술에 저:

  • 전념'Log 리트리버'개체할 수 있는 통과하여 로거 래퍼로만 부르 toString()를 필요한
  • 과 함께 사용되는 로깅 앞 기능 (또는 일반 Object[]배열!)

그리고 거기 당신은 그것에 의해 설명@존 Millikin 및@에릭슨.

그러나 이 문제는 우리에게 조금 생각에 대해'이유는 정확히 우리가 처음 로그인가?'
우리의 프로젝트가 실제로 30 개 프로젝트(5~10 사람들이 각)에 배치된 다양한 생산 플랫폼으로,비동기 통신의 요구와 중앙 버스 정류됩니다.
간단한 로깅을 설명에서 질문한 각 프로젝트 처음에 (5 년),그러나 그 이후,우리는 단계가 있습니다.입력 KPI.

신의 요청을 로그에,우리는 요청을 자동으로 만들어진 물체(라는 KPI)을 등록하는 이벤트입니다.그것은 간단한 통화(myKPI.I_am_signaling_myself_to_you()),그리고할 필요가 없는 조건부(를 해결하는'인공 증가 순환 복잡성'문제가 있습니다.)

는 KPI 개체가 알고 있는 사람은 그것을 이루고 그는 처음부터 실행 응용 프로그램의,그가를 검색할 수 있을 많은 양의 데이터를 우리는 이전에 컴퓨팅 자리에서 우리를 로깅입니다.
스피디아에는 취소 수수료가 없 KPI 개체 모니터링할 수 있는 독립적으로 컴퓨/에 게시하 수요 정보에 단일과 별도로 발행 버스입니다.
는 방법,각 클라이언트 요청할 수 있는 정보를 그가 실제로 원하는(좋아,'나의 프로세스 시작되었고,그렇다면,이후 언제입니까?'), 대한 올바른 로그 파일을 grepping 에 대한 복잡한 문자열을...

실제로,질문은'왜 정확히 우리가 처음 로그인가?'우리들을 깨닫리록하지 않는 그냥을 위한 프로그래머와 그 단위 또는 통합 테스트,하지만 훨씬 더 넓은 지역 사회의 일부를 포함하여 최종 클라이언트는 자신이다.우리의'보고'메커니즘을 집중되는,비동기,24/7.

특정의 것 KPI 메커니즘의 범위의 이 질문입니다.에 충분하고 적절한 교정해,손은 아래,단일의 가장 복잡은 비기능적인 문제는 우리가 직면하고 있다.그것은 여전히 가지고 시스템에 무릎에서 시간에 시간!을 올바로 교정하는 그러나,그것은 생명을 보호기입니다.

다시,당신을 감사에 대한 모든 제안입니다.우리는 그들을 고려할 것입니다에 대한 일부 부품의 경우 시스템에 간단한 로깅에서 여전히 장소입니다.
그러나 다른 시점의 이 질문은 설명하기 위해 당신에게 특정 문제에 훨씬 더 크고 더 복잡한다.
당신이 그것을 좋아 바랍니다.I 요청할 수 있습니다 질문에 KPI(는,믿거나 말거나,모든 질문에 SOF 지금까지!) 나중에 다음 주일입니다.

내가 이 답을 위해 투표할 때까지 다음 주 화요일에,나는 대답을 선택합니다.(이는 하나 분명히;))

어쩌면 이것은 너무 간단하지만,무엇을 사용하여"추출 방법"refactoring 드 주위에 절?귀하의 예제 코드의 this:

public void Example()
{
  if(myLogger.isLoggable(Level.INFO))
      myLogger.info("A String");
  if(myLogger.isLoggable(Level.FINE))
      myLogger.fine("A more complicated String");
  // +1 for each test and log message
}

다음이 됩니다.

public void Example()
{
   _LogInfo();
   _LogFine();
   // +0 for each test and log message
}

private void _LogInfo()
{
   if(!myLogger.isLoggable(Level.INFO))
      return;

   // Do your complex argument calculations/evaluations only when needed.
}

private void _LogFine(){ /* Ditto ... */ }

C 또는 C++에서 나를 사용하는 전처리기는 대신에 경제표에 대한 조건부 로그.

패 로그 수준을 기록하고 그것을 결정할지 여부를 작성하는 로그책:

//if(myLogger.isLoggable(Level.INFO) {myLogger.info("A String");
myLogger.info(Level.INFO,"A String");

업데이트:아,나는 당신이 원하는 조건에 따라 로그를 만들지 않고 문자열 조건문.아마도로 런타임이 아닌 컴파일때 정해진다.

다는 것이죠 우리가 해결이를 넣어 코드 서식 로거에 등록 서식만이 발생한 경우 수준을 전달합니다.와 매우 유사하는 내장 sprintf.예를 들어:

myLogger.info(Level.INFO,"A String %d",some_number);   

는 충족해야의 기준입니다.

체 텍스트 http://www.scala-lang.org/sites/default/files/newsflash_logo.png

Scala 는 annontation @elidable() 할 수 있는 제거 방법으로는 컴파일러 플래그입니다.

와 스칼라에 복제:

C:>밀라노

환하는 밀라노 버전 2.8.0.최종(Java HotSpot(TM)64-Bit Server VM,Java1.6.0_16).형 식에서 그들을 평가합니다.종류:자세한 내용은 도움말을.

밀라노>가져오는 스칼라.주석이 있습니다.elidable 가져오기 scala.주석이 있습니다.elidable

밀라노>가져오는 스칼라.주석이 있습니다.elidable._ 가져오기 scala.주석이 있습니다.elidable._

밀라노>@elidable(고급)def logDebug(arg:String)=system.out.println(arg)

logDebug:(arg:문자열)단위

밀라노>logDebug("시험")

밀라노>

와 엘리드-beloset

C:>밀라노-Xelide-0

환하는 밀라노 버전 2.8.0.최종(Java HotSpot(TM)64-Bit Server VM,Java1.6.0_16).형 식에서 그들을 평가합니다.종류:자세한 내용은 도움말을.

밀라노>가져오는 스칼라.주석이 있습니다.elidable 가져오기 scala.주석이 있습니다.elidable

밀라노>가져오는 스칼라.주석이 있습니다.elidable._ 가져오기 scala.주석이 있습니다.elidable._

밀라노>@elidable(고급)def logDebug(arg:String)=system.out.println(arg)

logDebug:(arg:문자열)단위

밀라노>logDebug("시험")

테스트

밀라노>

또한 참조 밀라노 주장 정의

조건부 로그한다.추가로 불필요한 혼란을 당신의 코드입니다.

당신은 항상에 보내는 객체가 있을 로거:

Logger logger = ...
logger.log(Level.DEBUG,"The foo is {0} and the bar is {1}",new Object[]{foo, bar});

다음 java.util.logging.포맷을 사용하는 MessageFormat 을 평평하게 foo 과 바로할 문자열을 출력한다.그것은 경우에만 호출됩니다 로거와 핸들러는 로그에는 수준입니다.

에 대한 추가 기쁨이 있는 어떤 종류의 식 언어를 얻을 수 있을 정밀하게 제어하는 형식으로 로그온한 물체(toString 지 않을 수도 유용합니다).

큼 싫어 매크로에서는 C/C++,직장에서 우리는#정의한 경우에는 경우 false 를 무시(산하지 않습니다)다음과 같은 표현하지만,true 이면 반 스트림으로는 할 수 있다는 파이프를 사용하'<<'연산자입니다.다음과 같다:

LOGGER(LEVEL_INFO) << "A String";

나는 가정이 제거하는 것이 여분의 복잡도는 당신의 도구를 보고도 모두 제거합을 계산하의 문자열,또는 어떤 식이 기록될 수 있는 경우에는 수준에 도달하지 않았습니다.

여기에는 우아한 솔루션을 사용하여 원현

logger.info(로거.isInfoEnabled()?"Log 문을 여기에...":null);

을 고려한 로깅 util 기능...

void debugUtil(String s, Object… args) {
   if (LOG.isDebugEnabled())
       LOG.debug(s, args);
   }
);

그런 다음 전화로"밈"라는 비싼 평가는 사용하고 싶지 않을 수도 있습니다.

debugUtil(“We got a %s”, new Object() {
       @Override String toString() { 
       // only evaluated if the debug statement is executed
           return expensiveCallToGetSomeValue().toString;
       }
    }
);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top