स्केल एक्सप्रेशन (इंस्ट्रूमेंटिंग) लॉगिंग

(@ strong> UPD @ krivachy.akos के बाद)

स्कैला में अभिव्यक्ति कैसे डीबग करें? आपके पास ब्रेकपॉइंट सेट करने और अधिकांश मामलों में स्थानीय चर देखने का अवसर नहीं है क्योंकि अभिव्यक्ति के अंदर कोई चर नहीं है। और आमतौर पर कोई बयान नहीं है जिसके लिए आप ब्रेकपॉइंट सेट कर सकते हैं।

डीबगिंग का एक पुराना तरीका कोड का वाद्य यंत्र होना है। यह अभिव्यक्तियों की आंतरिक प्रसंस्करण के बारे में एक अनिवार्य जानकारी देता है।

हालांकि, एक सामान्य लॉगिंग कार्यान्वयन में अभिव्यक्तियों को रोकने का कोई प्रत्यक्ष तरीका नहीं है। विशेष रूप से, एक ठेठ लॉगर में इकाई वापसी प्रकार के साथ विधियां होती हैं:

def debug(msg: =>String) {...}

लॉगर का उपयोग करने के लिए लॉगजर को कॉल करने में सक्षम होने के लिए किसी को संक्षिप्त अभिव्यक्ति को फिर से लिखना होगा:

Example 1:

यदि आपके पास जटिल परिस्थितियों और एकाधिक मूल्यांकन पथों के साथ कुछ बूलियन-आधारित नियम हैं:

val x = if(condition1(a,b)) 
          Some(production1(a,b))
        else if(condition2(c,d))
          Some(production2(a,b))
        else
          None

तो यह सुनिश्चित करना मुश्किल है कि यह वांछित के रूप में काम करता है। (जटिल नियमों से पूरी तरह से बचना हमेशा संभव नहीं होता है। और ओओपी-शैली में प्रतिनिधित्व नियम हमेशा अच्छे नहीं होते हैं।)

फिर एक ठेठ उपकरण के लिए कुछ मध्यवर्ती चर और कोड के ब्लॉक की आवश्यकता होगी:

debug("a="+a)
debug("b="+b)
val x = if(condition1(a,b)) {
          debug("rule1 hit")
          val production = production1(a,b)
          debug("rule1 result: "+production)
          Some(production)
        } else { 
          debug("rule1 doesn't hit")
          debug("c="+c)
          debug("d="+d)
          if(condition2(c,d)){
            debug("rule2 hit")
            Some(production2(a,b))
          } else
            None
        }

Example 2:

def f(list:List[Int]) = 
    list.
        map(_*2).
        flatMap(t =>
            (0 until t).
            map(_+1)
        )

इंस्ट्रुमेंटेशन कुछ मध्यवर्ती चर का कारण बन जाएगा:

def f(list:List[Int]) = {        
    val twiced = list.map(_*2)
    debug(s"twiced = $twiced")
    val result = twiced.flatMap(t => {
        val widened = (0 until t).map(_+1)            
        debug(s"widened = $widened")
        widened
    })
    debug(s"result = $result")
    result
}

बहुत बदसूरत, मुझे लगता है। और यह उपकरण कोड की तुलना में अधिक जगह लेता है। मुझे लगता है कि मुख्य कारण यह है कि लॉगजर अभिव्यक्ति मूल्यांकन शैली के साथ असंगत है।

अभिव्यक्ति मूल्यों को अधिक संक्षिप्त तरीके से लॉग करने का कोई तरीका है?

0
जोड़ा संपादित
विचारों: 1

1 उत्तर

मुझे हाल ही में अभिव्यक्ति के मूल्य को लॉग करने का एक अच्छा तरीका मिला है:

trait Logging {

    protected val loggerName = getClass.getName
    protected lazy val logger = LoggerFactory.getLogger(loggerName)

    implicit class RichExpressionForLogging[T](expr: T){
        def infoE (msg: T ⇒ String):T = {if (logger.isInfoEnabled ) logger.info (msg(expr)); expr}
        def traceE(msg: T ⇒ String):T = {if (logger.isTraceEnabled) logger.trace(msg(expr)); expr}
        def debugE(msg: T ⇒ String):T = {if (logger.isDebugEnabled) logger.debug(msg(expr)); expr}      
        def infoL (msg: String):T = {if (logger.isInfoEnabled ) logger.info (msg+expr); expr}
        def traceL(msg: String):T = {if (logger.isTraceEnabled) logger.trace(msg+expr); expr}
        def debugL(msg: String):T = {if (logger.isDebugEnabled) logger.debug(msg+expr); expr}       
    }

}

यहां इसका उपयोग कैसे किया जाता है:

उदाहरण 1 (नियम):

val x = if(condition1(a.debugL("a="),b.debugL("b="))) 
      Some(production1(a,b).debugL("rule1="))
    else if(condition2(c,d))
      Some(production2(a,b).debugL("rule2="))
    else
      None

उदाहरण 2:

def f(list:List[Int]) = 
    list.
        map(_*2).
        debugE(s"res1="+_).
        flatMap(t => (0 until t).
            map(_+1).
            debugE(s"res2="+_)).
        debugE(s"res="+_)

यह अभिव्यक्तियों में हर जगह भी इस्तेमाल किया जा सकता है:

if((a<0).debugE(s"a=$a<0="+_))

for{
    a <- f(list).debugE("f(list)="+_)
    b <- a.debugL("a=")
} yield b.debugL("b=")

बेशक आपको अपनी कक्षा में लॉगिंग विशेषता मिश्रण करना चाहिए।

इस प्रकार का वाद्ययंत्र कोड के तर्क को छिपाता नहीं है।

0
जोड़ा
यह बॉयलरप्लेट के बिना अभिव्यक्ति मान लॉग इन करने की अनुमति देता है। और सीधे एक अभिव्यक्ति में एम्बेडेड किया जा सकता है।
जोड़ा लेखक Arseniy Zhizhelev, स्रोत
खैर, मुझे और वास्तविक जीवन अभिव्यक्तियां डालनी चाहिए थीं। अभिव्यक्ति के उदाहरण थोड़ा कृत्रिम हैं। मुद्दा यह है कि अभिव्यक्ति मूल्यांकन में मध्यवर्ती vals के साथ इसे फिर से लिखने के बिना कुछ रास्ता तय करना है।
जोड़ा लेखक Arseniy Zhizhelev, स्रोत
ई अभिव्यक्ति, एल लेबल। मैं सरल डीबग का उपयोग नहीं कर सका क्योंकि यह पहले से ही : यूनिट संस्करण के लिए आरक्षित है। आप विधि नामों के लिए अन्य नाम सुझा सकते हैं।
जोड़ा लेखक Arseniy Zhizhelev, स्रोत
आपका समाधान अच्छा है, लेकिन निश्चित रूप से अच्छा नहीं है। यह खराब कोडिंग प्रथाओं को दूर करने के लिए एक समाधान की तरह लगता है। मैं कहूंगा कि यदि आप एक साथ कई तरीकों को श्रृंखलाबद्ध करते हैं और वास्तव में अपने कोड को डीबग करने के लिए लॉगिंग में हैक करने की आवश्यकता है तो आप नहीं जानते कि आपका कोड वास्तव में क्या करता है । अच्छा और स्पष्ट स्कैला कोड इंटरमीडिएट वेरिएबल्स हैं जिन्हें उपयुक्त रूप से नामित किया गया है अन्य प्रोग्रामर (और आप) को यह समझने के लिए कि कोड क्या करता है। कृपया 42:00 से
जोड़ा लेखक Akos Krivachy, स्रोत
@ArseniyZhizhelev ठीक है, मुझे लगता है कि यह अब थोड़ा और समझ में आता है। बीटीडब्ल्यू, विधियों के अंत में E और L क्या खड़ा है?
जोड़ा लेखक Akos Krivachy, स्रोत