فهرست منبع

HPCC-20874 Support transaction_log_online in ESP logging agent

1. Support ESDL commands: bind-log-transform/unbind-log-transform;
2. Add/set ESDLBindingID to ESP Context which is needed for reading
   log-transform xsl from ESDL binding;
3. In wslogserviceespagent.xsl: add LogDataItem as a setting group of
   wslogserviceesp logging agent. Move hardcoded logging items from
   the logDataXPath settings into this setting group. A user may add
   more logging item into the group.
4. In wslogserviceespagent.xsl: modify the logging item settings
   inside the LogInfo group.
5. In wslogserviceespagent.xsl: add setting for default logging
   values and xsl name.

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 6 سال پیش
والد
کامیت
6a93649a3f

+ 4 - 0
esp/logging/loggingmanager/loggingmanager.cpp

@@ -162,8 +162,12 @@ bool CLoggingManager::updateLog(IEspContext* espContext, const char* option, IPr
 
             short port;
             StringBuffer sourceIP;
+            const char* esdlBindingID = espContext->queryESDLBindingID();
             espContext->getServAddress(sourceIP, port);
             espContextTree->addProp("SourceIP", sourceIP.str());
+            if (!isEmptyString(esdlBindingID))
+                espContextTree->addProp("ESDLBindingID", esdlBindingID);
+            //More information in espContext may be added to the espContextTree later.
 
             const char* userId = espContext->queryUserId();
             if (userId && *userId)

+ 10 - 0
esp/platform/espcontext.cpp

@@ -43,6 +43,7 @@ private:
     StringAttr      m_acceptLanguage;
     StringAttr      httpMethod;
     StringAttr      servMethod;
+    StringAttr      esdlBindingID;
 
     StringBuffer    m_servName;
     StringBuffer    m_servHost;
@@ -477,6 +478,15 @@ public:
         servMethod.set(method);
     }
 
+    virtual void setESDLBindingID(const char *id)
+    {
+        esdlBindingID.set(id);
+    }
+    virtual const char* queryESDLBindingID()
+    {
+        return esdlBindingID.get();
+    }
+
     virtual CTxSummary* queryTxSummary()
     {
         return m_txSummary.get();

+ 3 - 0
esp/scm/esp.ecm

@@ -191,6 +191,9 @@ interface IEspContext : extends IInterface
     virtual void setHTTPMethod(const char *method) = 0;
     virtual void setServiceMethod(const char *method) = 0;
 
+    virtual void setESDLBindingID(const char * id) = 0;
+    virtual const char * queryESDLBindingID() = 0;
+
     virtual void setTransactionID(const char * trxid) = 0;
     virtual const char * queryTransactionID() = 0;
 

+ 26 - 2
esp/scm/ws_esdlconfig.ecm

@@ -209,6 +209,29 @@ ESPresponse [nil_remove, exceptions_inline] ConfigureESDLBindingMethodResponse
     [min_ver("1.2")] ESPStruct ESDLBindingContents ESDLBinding;
 };
 
+ESPrequest [nil_remove] ConfigureESDLBindingLogTransformRequest
+{
+    string EsdlBindingId;
+    string LogTransformName;
+    boolean Overwrite;
+    boolean Encoded(false); //The LogTransform in the Config is not encoded.
+    string Config; // dynamic xml, can have <Binding EspProcess=xxx EspBinding=WsAccurint><Definition name=xx id=xx.yy><LogTransforms><LogTransform>...
+                   //              or  <Definition name=xx id=xx.yy><LogTransforms><LogTransform>...
+                   //              or  <LogTransforms><LogTransform>....
+                   //<LogTransforms><LogTransform name="name">...</LogTransform><LogTransforms>
+    boolean EchoBinding;
+};
+
+ESPresponse [nil_remove, exceptions_inline] ConfigureESDLBindingLogTransformResponse
+{
+    string EspProcName;         //Name of ESP Process
+    string EspBindingName;
+    string EsdlDefinitionID;    //The ESDL definition name.ver
+    string EsdlServiceName;     //which ESDL definition are we configuring
+    ESPstruct BaseESDLStatus status;
+    ESPStruct ESDLBindingContents ESDLBinding;
+};
+
 ESPrequest GetESDLBindingRequest
 {
     string EsdlBindingId;
@@ -282,13 +305,14 @@ ESPresponse [exceptions_inline] ListESDLBindingsResponse
     [min_ver("1.4")] ESParray<ESPstruct EspProcessStruct, EspProcess> EspProcesses;
 };
 
-#define VERSION_FOR_ESDLCMD "1.4"
-ESPservice [auth_feature("ESDLConfigAccess:ACCESS"), version("1.4"), exceptions_inline("./smc_xslt/exceptions.xslt")] WsESDLConfig
+#define VERSION_FOR_ESDLCMD "1.5"
+ESPservice [auth_feature("ESDLConfigAccess:ACCESS"), version("1.5"), exceptions_inline("./smc_xslt/exceptions.xslt")] WsESDLConfig
 {
     ESPmethod Echo(EchoRequest, EchoResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:WRITE")] PublishESDLDefinition(PublishESDLDefinitionRequest, PublishESDLDefinitionResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:WRITE")] PublishESDLBinding(PublishESDLBindingRequest, PublishESDLBindingResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:WRITE")] ConfigureESDLBindingMethod(ConfigureESDLBindingMethodRequest, ConfigureESDLBindingMethodResponse);
+    ESPmethod [auth_feature("ESDLConfigAccess:WRITE"), min_ver("1.5")] ConfigureESDLBindingLogTransform(ConfigureESDLBindingLogTransformRequest, ConfigureESDLBindingLogTransformResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:READ")]  GetESDLBinding(GetESDLBindingRequest, GetESDLBindingResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:FULL")]  DeleteESDLBinding(DeleteESDLBindingRequest, DeleteESDLRegistryEntryResponse);
     ESPmethod [auth_feature("ESDLConfigAccess:FULL")]  DeleteESDLDefinition(DeleteESDLDefinitionRequest, DeleteESDLRegistryEntryResponse);

+ 4 - 2
esp/services/esdl_svc_engine/esdl_binding.cpp

@@ -1692,6 +1692,7 @@ int EsdlBindingImpl::onGetInstantQuery(IEspContext &context,
                     generateNamespace(context, request, srvdef->queryName(), mthdef->queryName(), ns);
 
                     getSchemaLocation(context, request, schemaLocation);
+                    context.setESDLBindingID(m_bindingId.get());
                     m_pESDLService->handleServiceRequest(context, *srvdef, *mthdef, tgtcfg, tgtctx, ns.str(), schemaLocation.str(), req_pt.get(), out, logdata, 0);
 
                     response->setContent(out.str());
@@ -1993,8 +1994,8 @@ int EsdlBindingImpl::HandleSoapRequest(CHttpRequest* request,
             generateNamespace(*ctx, request, srvdef->queryName(), mthdef->queryName(), ns);
             getSchemaLocation(*ctx, request, schemaLocation);
 
-             m_pESDLService->handleServiceRequest(*ctx, *srvdef, *mthdef, tgtcfg, tgtctx, ns.str(),
-                                       schemaLocation.str(), pt, baseout, logdata, 0);
+            ctx->setESDLBindingID(m_bindingId.get());
+            m_pESDLService->handleServiceRequest(*ctx, *srvdef, *mthdef, tgtcfg, tgtctx, ns.str(), schemaLocation.str(), pt, baseout, logdata, 0);
 
             StringBuffer out;
             out.append(
@@ -2714,6 +2715,7 @@ void EsdlBindingImpl::handleJSONPost(CHttpRequest *request, CHttpResponse *respo
                         generateNamespace(*ctx, request, serviceName, methodName, ns);
                         getSchemaLocation(*ctx, request, schemaLocation);
 
+                        ctx->setESDLBindingID(m_bindingId.get());
                         m_pESDLService->handleServiceRequest(*ctx, *srvdef, *mthdef, tgtcfg, tgtctx, ns.str(), schemaLocation.str(), reqTree.get(), jsonresp, logdata, ESDL_BINDING_RESPONSE_JSON);
 
                         jsonresp.append("}");

+ 94 - 0
esp/services/esdl_svc_engine/esdl_store.cpp

@@ -434,6 +434,99 @@ public:
             return configureMethod(bindingId, methodName, configTree, overwrite, message);
     }
 
+    virtual int configureLogTransform(const char* bindingId, const char* logTransformName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) override
+    {
+        if (isEmptyString(bindingId))
+        {
+            message.set("Unable to configure method, binding id must be provided");
+            return -1;
+        }
+        if (!configTree)
+        {
+            message.setf("Unable to configure method '%s', configuration attributes must be provided", logTransformName);
+            return -1;
+        }
+
+        VStringBuffer rxpath("%sBinding[@id='%s']/Definition/LogTransforms[1]", ESDL_BINDINGS_ROOT_PATH, bindingId);
+        Owned<IRemoteConnection> conn;
+
+        try
+        {
+            conn.setown(querySDS().connect(rxpath, myProcessSession(), RTM_LOCK_WRITE, SDS_LOCK_TIMEOUT_DESDL));
+        }
+        catch (ISDSException * e)
+        {
+            StringBuffer msg;
+            e->errorMessage(msg);
+            if (msg.isEmpty())
+                message.setf("Unable to operate on Dali path: %s", rxpath.str());
+            else
+                message.setf("Unable to operate on Dali path: %s. %s", rxpath.str(), msg.str());
+            e->Release();
+            return -1;
+        }
+
+        //Only lock the branch for the target we're interested in.
+        if (!conn)
+            throw MakeStringException(-1, "Unable to connect to %s", rxpath.str());
+
+        Owned<IPropertyTree> root = conn->getRoot();
+        if (!root.get())
+            throw MakeStringException(-1, "Unable to open %s", rxpath.str());
+
+        VStringBuffer xpath("LogTransform[@name='%s']", logTransformName);
+        Owned<IPropertyTree> oldEnvironment = root->getPropTree(xpath.str());
+        if (oldEnvironment.get())
+        {
+            if (overwrite)
+            {
+                message.set("Existing LogTransform configuration overwritten!");
+                root->removeTree(oldEnvironment);
+            }
+            else
+            {
+                message.set("LogTransform configuration exists will not overwrite!");
+                return -1;
+            }
+        }
+
+        root->addPropTree("LogTransform", configTree);
+        conn->commit();
+
+        VStringBuffer changestr("action=update;type=binding;targetId=%s", bindingId);
+        triggerSubscription(changestr.str());
+
+        message.appendf("\nSuccessfully configured Method '%s' for binding '%s'", logTransformName, bindingId);
+        return 0;
+    }
+
+    virtual int configureLogTransform(const char* espProcName, const char* espBindingName, const char* definitionId, const char* logTransformName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) override
+    {
+        if (isEmptyString(espProcName))
+        {
+            message.set("Unable to configure method, ESP Process Name not available");
+            return -1;
+        }
+        if (isEmptyString(espBindingName))
+        {
+            message.set("Unable to configure method, ESP Binding Name not available");
+            return -1;
+        }
+
+        if (isEmptyString(definitionId))
+        {
+            message.set("Unable to configure method, ESDL definition ID not available");
+            return -1;
+        }
+
+        StringBuffer bindingId;
+        getIdFromProcBindingDef(espProcName, espBindingName, definitionId, bindingId, message);
+        if (bindingId.isEmpty())
+            return -1;
+
+        return configureLogTransform(bindingId, logTransformName, configTree, overwrite, message);
+    }
+
     virtual int bindService(const char* bindingName,
                                              IPropertyTree* methodsConfig,
                                              const char* espProcName,
@@ -649,6 +742,7 @@ public:
         else
             esdldeftree->addPropTree("Methods");
 
+        esdldeftree->addPropTree("LogTransforms");
         bindingtree->addPropTree(ESDL_DEF_ENTRY, LINK(esdldeftree));
         bindings->addPropTree(ESDL_BINDING_ENTRY, LINK(bindingtree));
 

+ 2 - 0
esp/services/esdl_svc_engine/esdl_store.hpp

@@ -40,6 +40,8 @@ interface IEsdlStore : public IInterface
     virtual bool addDefinition(const char* definitionName, IPropertyTree* definitionInfo, StringBuffer &newId, unsigned &newSeq, const char* userid, bool deleteprev, StringBuffer & message) = 0;
     virtual int configureMethod(const char* bindingId, const char* methodName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) = 0;
     virtual int configureMethod(const char* espProcName, const char* espBindingName, const char* definitionId, const char* methodName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) = 0;
+    virtual int configureLogTransform(const char* bindingId, const char* logTransformName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) = 0;
+    virtual int configureLogTransform(const char* espProcName, const char* espBindingName, const char* definitionId, const char* logTransformName, IPropertyTree* configTree, bool overwrite, StringBuffer& message) = 0;
     virtual int bindService(const char* bindingName, IPropertyTree* methodsConfig, const char* espProcName, const char* espPort, const char* definitionId,
                        const char* esdlServiceName, StringBuffer& message, bool overwrite, const char* user) = 0;
     virtual bool deleteDefinition(const char* definitionId, StringBuffer& errmsg, StringBuffer* defxml) = 0;

+ 132 - 10
esp/services/ws_esdlconfig/ws_esdlconfigservice.cpp

@@ -26,6 +26,12 @@
 #include "esdl_binding.hpp"
 #include "TpWrapper.hpp"
 
+enum ESDLConfigInfoType
+{
+    ESDLMethod = 0,
+    ESDLLogTransform = 1,
+};
+
 IPropertyTree * fetchConfigInfo(const char * config,
                                 StringBuffer & espProcName,
                                 StringBuffer & espBindingName,
@@ -117,9 +123,15 @@ IPropertyTree * fetchConfigInfo(const char * config,
 }
 
 IPropertyTree * fetchConfigInfo(const char * config,
-                                const char* bindingId)
+                                const char* bindingId,
+                                ESDLConfigInfoType infoType = ESDLMethod)
 {
-    IPropertyTree * methodstree = NULL;
+    IPropertyTree * retTree = nullptr;
+    StringAttr treeName;
+    if (infoType == ESDLMethod)
+        treeName.set("Methods");
+    else
+        treeName.set("LogTransforms");
 
     if (!config || !*config)
     {
@@ -127,7 +139,7 @@ IPropertyTree * fetchConfigInfo(const char * config,
     }
     else
     {
-        Owned<IPropertyTree>  configTree = createPTreeFromXMLString(config, ipt_caseInsensitive);
+        Owned<IPropertyTree>  configTree = createPTreeFromXMLString(config, ipt_caseInsensitive | ipt_ordered);
         //Now let's figure out the structure of the configuration passed in...
 
         StringBuffer rootname;
@@ -146,17 +158,15 @@ IPropertyTree * fetchConfigInfo(const char * config,
             deftree = configTree->queryBranch("Definition[1]");
 
         if (deftree)
-        {
-            methodstree = deftree->getBranch("Methods");
-        }
+            retTree = deftree->getBranch(treeName.get());
 
-        if (!methodstree) //if we didn't already find the methods section of the config, let's look at the root
+        if (!retTree) //if we didn't already find the methods or log-transforms section of the config, let's look at the root
         {
-            if (stricmp(rootname.str(), "Methods") == 0)
-                methodstree = configTree.getLink();
+            if (stricmp(rootname.str(), treeName.get()) == 0)
+                retTree = configTree.getLink();
         }
     }
-    return methodstree;
+    return retTree;
 }
 
 void CWsESDLConfigEx::init(IPropertyTree *cfg, const char *process, const char *service)
@@ -949,6 +959,118 @@ bool CWsESDLConfigEx::onConfigureESDLBindingMethod(IEspContext &context, IEspCon
     return true;
 }
 
+bool CWsESDLConfigEx::onConfigureESDLBindingLogTransform(IEspContext &context, IEspConfigureESDLBindingLogTransformRequest &req, IEspConfigureESDLBindingLogTransformResponse &resp)
+{
+    int success = 0;
+    try
+    {
+        if (m_isDetachedFromDali)
+            throw MakeStringException(-1, "Cannot Configure ESDL Binding LogTransform. ESP is currently detached from DALI.");
+
+        context.ensureFeatureAccess(FEATURE_URL, SecAccess_Write, ECLWATCH_ROXIE_QUERY_ACCESS_DENIED, "WsESDLConfigEx::ConfigureESDLBindingLogTransform: Permission denied.");
+
+        StringBuffer username;
+        context.getUserID(username);
+        DBGLOG("CWsESDLConfigEx::onConfigureESDBindingLogTransform User=%s",username.str());
+
+        const char* bindingId = req.getEsdlBindingId();
+        const char* logTransformName = req.getLogTransformName();
+        const char* logTransform = req.getConfig();
+        if (isEmptyString(logTransform))
+            throw MakeStringException(-1, "Config not defined.");
+
+        StringBuffer espProcName;
+        StringBuffer espBindingName;
+        StringBuffer esdlServiceName;
+        StringBuffer esdlDefIdSTR;
+        StringBuffer config;
+        if (strncmp(logTransform, "<LogTransforms>", 15) == 0)
+            config.set(logTransform);
+        else if (isEmptyString(logTransformName))
+            throw MakeStringException(-1, "LogTransformName not defined");
+        else
+        {
+            config.appendf("<LogTransforms><LogTransform name='%s'>", logTransformName);
+            if (req.getEncoded())
+                config.append(logTransform);
+            else
+            {
+                StringBuffer encodedLogTransform;
+                encodeXML(logTransform, encodedLogTransform);
+                config.append(encodedLogTransform.str());
+            }
+            config.append("</LogTransform></LogTransforms>");
+        }
+
+        StringBuffer msg;
+        Owned<IPropertyTree> bindingtree = m_esdlStore->getBindingTree(bindingId, msg);
+        if(!bindingtree)
+            throw MakeStringException(-1, "Can't find esdl binding for id %s", bindingId);
+
+        bindingtree->getProp("@espprocess", espProcName);
+        bindingtree->getProp("@espbinding", espBindingName);
+        bindingtree->getProp("Definition[1]/@esdlservice", esdlServiceName);
+        bindingtree->getProp("Definition[1]/@id", esdlDefIdSTR);
+
+        Owned<IPropertyTree> logTransformTree = fetchConfigInfo(config, bindingId, ESDLLogTransform);
+        if (!logTransformTree || logTransformTree->getCount("LogTransform") <= 0)
+            throw MakeStringException(-1, "Could not find any LogTransform configuration entries.");
+
+        const char *esdlDefId = esdlDefIdSTR.str();
+        if (isEmptyString(esdlDefId))
+            throw MakeStringException(-1, "Can't find esdl definition for binding %s", bindingId);
+
+        StringBuffer esdlDefinitionName;
+        while (esdlDefId && *esdlDefId != '.')
+            esdlDefinitionName.append(*esdlDefId++);
+
+        if (isEmptyString(esdlDefId))
+            throw MakeStringException(-1, "Invalid ESDL Definition ID format detected: '%s'. Expected format: <esdldefname>.<ver>", esdlDefIdSTR.str());
+
+        int esdlver = 0;
+        esdlDefId++;
+        if (esdlDefId)
+            esdlver = atoi(esdlDefId);
+
+        if (esdlver <= 0)
+            throw MakeStringException(-1, "Invalid ESDL Definition version detected: %d", esdlver);
+
+        if (!m_esdlStore->definitionExists(esdlDefIdSTR.str()))
+            throw MakeStringException(-1, "Invalid ESDL Definition: %s", esdlDefIdSTR.str());
+
+        bool override = req.getOverwrite();
+        StringBuffer status;
+        StringBuffer logTransformXPath;
+        if (!isEmptyString(logTransformName))
+            logTransformXPath.appendf("LogTransform[@name='%s']", logTransformName);
+        else
+            logTransformXPath.append("LogTransform");
+        Owned<IPropertyTreeIterator> iter = logTransformTree->getElements(logTransformXPath.str());
+        ForEach(*iter)
+        {
+            IPropertyTree &item = iter->query();
+            const char* ltName = item.queryProp("@name");
+            StringBuffer msg;
+            success = m_esdlStore->configureLogTransform(bindingId, logTransformName, LINK(&item), override, msg);
+            status.appendf("%s: %s; ", ltName, msg.str());
+        }
+        resp.updateStatus().setDescription(status.str());
+
+        resp.setEspProcName(espProcName.str());
+        resp.setEspBindingName(espBindingName.str());
+        resp.setEsdlDefinitionID(esdlDefIdSTR.str());
+        resp.setEsdlServiceName(esdlServiceName.str());
+    }
+    catch(IException* e)
+    {
+       FORWARDEXCEPTION(context, e, -1);
+    }
+
+    resp.updateStatus().setCode(success);
+
+    return true;
+}
+
 int CWsESDLConfigEx::getBindingXML(const char * bindingId, StringBuffer & bindingXml, StringBuffer & msg)
 {
     Owned<IPropertyTree> esdlBinding = m_esdlStore->getBindingTree(bindingId, msg);

+ 1 - 0
esp/services/ws_esdlconfig/ws_esdlconfigservice.hpp

@@ -48,6 +48,7 @@ public:
     bool onPublishESDLDefinition(IEspContext &context, IEspPublishESDLDefinitionRequest &req, IEspPublishESDLDefinitionResponse &resp);
     bool onPublishESDLBinding(IEspContext &context, IEspPublishESDLBindingRequest &req, IEspPublishESDLBindingResponse &resp);
     bool onConfigureESDLBindingMethod(IEspContext &context, IEspConfigureESDLBindingMethodRequest &req, IEspConfigureESDLBindingMethodResponse &resp);
+    bool onConfigureESDLBindingLogTransform(IEspContext &context, IEspConfigureESDLBindingLogTransformRequest &req, IEspConfigureESDLBindingLogTransformResponse &resp);
     bool onDeleteESDLBinding(IEspContext &context, IEspDeleteESDLBindingRequest &req, IEspDeleteESDLRegistryEntryResponse &resp);
     bool onDeleteESDLDefinition(IEspContext &context, IEspDeleteESDLDefinitionRequest &req, IEspDeleteESDLRegistryEntryResponse &resp);
     bool onGetESDLDefinition(IEspContext &context, IEspGetESDLDefinitionRequest&req, IEspGetESDLDefinitionResponse &resp);

+ 5 - 39
initfiles/componentfiles/configxml/@temp/wslogserviceespagent.xsl

@@ -36,8 +36,7 @@ xmlns:set="http://exslt.org/sets">
         <xsl:if test="string($loggingServerUrl) = ''">
             <xsl:message terminate="yes">Logging Server URL is undefined for <xsl:value-of select="$agentName"/>!</xsl:message>
         </xsl:if>
-        <xsl:variable name="logDataXPath" select="$agentNode/LogDataXPath"/>
-        <xsl:if test="not($logDataXPath)">
+        <xsl:if test="not($agentNode/LogDataItem[1]) and not($agentNode/LogInfo[1])">
             <xsl:message terminate="yes">Log Data XPath is undefined for <xsl:value-of select="$agentName"/> </xsl:message>
         </xsl:if>
 
@@ -73,44 +72,11 @@ xmlns:set="http://exslt.org/sets">
             </xsl:call-template>
                 
             <LogDataXPath>
-                <xsl:if test="string($logDataXPath/@IP) != ''">
-                    <IP><xsl:value-of select="$logDataXPath/@IP"/></IP>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@UserName) != ''">
-                    <UserName><xsl:value-of select="$logDataXPath/@UserName"/></UserName>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@ServiceName) != ''">
-                    <ServiceName><xsl:value-of select="$logDataXPath/@ServiceName"/></ServiceName>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@RecordCount) != ''">
-                    <RecordCount><xsl:value-of select="$logDataXPath/@RecordCount"/></RecordCount>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@DomainName) != ''">
-                    <DomainName><xsl:value-of select="$logDataXPath/@DomainName"/></DomainName>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@GUID) != ''">
-                    <GUID><xsl:value-of select="$logDataXPath/@GUID"/></GUID>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@BlindLogging) != ''">
-                    <BlindLogging><xsl:value-of select="$logDataXPath/@BlindLogging"/></BlindLogging>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@EncryptedLogging) != ''">
-                    <EncryptedLogging><xsl:value-of select="$logDataXPath/@EncryptedLogging"/></EncryptedLogging>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@ForwardLog) != ''">
-                    <ForwardLog><xsl:value-of select="$logDataXPath/@ForwardLog"/></ForwardLog>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@RawLogInformation) != ''">
-                    <RawLogInformation><xsl:value-of select="$logDataXPath/@RawLogInformation"/></RawLogInformation>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@CompressBlobs) != ''">
-                    <CompressBlobs><xsl:value-of select="$logDataXPath/@CompressBlobs"/></CompressBlobs>
-                </xsl:if>
-                <xsl:if test="string($logDataXPath/@WorkunitID) != ''">
-                    <WorkunitID><xsl:value-of select="$logDataXPath/@WorkunitID"/></WorkunitID>
-                </xsl:if>
+                <xsl:for-each select="$agentNode/LogDataItem">
+                    <LogDataItem name="{current()/@name}" XPath="{current()/@xpath}" xsl="{current()/@xsl}" default="{current()/@default}"/>
+                </xsl:for-each>
                 <xsl:for-each select="$agentNode/LogInfo">
-                    <LogInfo name="{current()/@name}" valueXPath="{current()/@valueXPath}" dataXPath="{current()/@dataXPath}" multipleValue="{current()/@multipleValue}" multipleData="{current()/@multipleData}" encodeValue="{current()/@encodeValue}" encodeData="{current()/@encodeData}"/>
+                    <LogInfo name="{current()/@name}" default="{current()/@default}" XPath="{current()/@xpath}" xsl="{current()/@xsl}" multiple="{current()/@multiple}" encode="{current()/@encode}" type="{current()/@type}"/>
                 </xsl:for-each>
             </LogDataXPath>
         </LogAgent>

+ 308 - 0
tools/esdlcmd/esdl-publish.cpp

@@ -1095,6 +1095,314 @@ public:
     }
 };
 
+class EsdlBindLogTransformCmd : public EsdlBindServiceCmd
+{
+protected:
+    StringAttr optLogTransform;
+    StringAttr optBindingId;
+    bool       optEncoded;
+
+public:
+    int processCMD()
+    {
+        Owned<IClientWsESDLConfig> esdlConfigClient = EsdlCmdHelper::getWsESDLConfigSoapService(optWSProcAddress, optWSProcPort, optUser, optPass);
+        Owned<IClientConfigureESDLBindingLogTransformRequest> request = esdlConfigClient->createConfigureESDLBindingLogTransformRequest();
+
+        fprintf(stdout,"\nAttempting to configure LogTransform : '%s' for binding '%s'\n", optLogTransform.get(), optBindingId.get());
+        request->setEsdlBindingId(optBindingId.get());
+        request->setLogTransformName(optLogTransform.get());
+        request->setConfig(optInput);
+        request->setOverwrite(optOverWrite);
+        request->setEncoded(optEncoded);
+
+        if (optVerbose)
+            fprintf(stdout,"\nLogTransform config: %s\n", optInput.get());
+
+        Owned<IClientConfigureESDLBindingLogTransformResponse> resp = esdlConfigClient->ConfigureESDLBindingLogTransform(request);
+
+        if (resp->getExceptions().ordinality()>0)
+        {
+            EsdlCmdHelper::outputMultiExceptions(resp->getExceptions());
+            return 1;
+        }
+
+        outputWsStatus(resp->getStatus().getCode(), resp->getStatus().getDescription());
+
+        return 0;
+    }
+
+    void usage()
+    {
+        printf( "\nUsage:\n\n"
+                "esdl bind-log-transform <TargetESDLBindingID> [TargetLogTransformName] --config <file|xml> [command options]\n\n"
+                "   TargetESDLBindingID                              The id of the target ESDL binding (must exist in dali)\n"
+                "   TargetLogTransformName                           The name of the target LogTransform (required when config is xsl transform)\n"
+                "   --config <file|xml>                              Configuration XML for all LogTransforms associated with the target Service\n"
+
+                "\nOptions (use option flag followed by appropriate value):\n"
+                "   --overwrite                                      Overwrite LogTransform if it already exists\n"
+                "   --encoded                                        The LogTransform in config has been encoded.\n");
+
+                EsdlPublishCmdCommon::usage();
+
+        printf( "\n Use this command to publish ESDL Service based bindings.\n"
+                "   To bind a ESDL Service, provide the id of the esdl binding and the LogTransform name you're going to configure.\n"
+                "   Optionally provide configuration information either directly, or via a\n"
+                "   configuration file in the following syntax:\n"
+                "     <LogTransforms>\n"
+                "     \t<LogTransform name=\"myLogTransform\">escaped transform script</LogTransform>\n"
+                "     \t<LogTransform name=\"myLogTransform2\">escaped transform script</LogTransform>\n"
+                "     </LogTransforms>\n"
+                "   or\n"
+                "     one transform script\n"
+                );
+
+        printf("\nExample:\n"
+                ">esdl bind-log-transform myesp.8003.EsdlExample myLogTransform --config /myService/log-transforms.xml\n"
+                "or:\n"
+                ">esdl bind-log-transform myesp.8003.EsdlExample myLogTransform --config /myService/log-transform.xsl\n"
+                );
+    }
+
+    bool parseCommandLineOptions(ArgvIterator &iter)
+    {
+        if (iter.done())
+        {
+            usage();
+            return false;
+        }
+
+        //First 2 parameter order is fixed.
+        for (int cur = 0; cur < 2 && !iter.done(); cur++)
+        {
+           const char *arg = iter.query();
+           if (*arg != '-')
+           {
+               switch (cur)
+               {
+                case 0:
+                    optBindingId.set(arg);
+                    break;
+                case 1:
+                    optLogTransform.set(arg);
+                    break;
+               }
+           }
+           else
+           {
+               fprintf(stderr, "\noption detected before required arguments: %s\n", arg);
+               usage();
+               return false;
+           }
+
+           iter.next();
+        }
+
+        for (; !iter.done(); iter.next())
+        {
+            if (parseCommandLineOption(iter))
+                continue;
+
+            if (matchCommandLineOption(iter, true)!=EsdlCmdOptionMatch)
+                return false;
+        }
+
+        return true;
+    }
+
+    bool parseCommandLineOption(ArgvIterator &iter)
+    {
+        if (iter.matchFlag(optEncoded, ESDL_OPTION_ENCODED) )
+            return true;
+
+        if (EsdlBindServiceCmd::parseCommandLineOption(iter))
+            return true;
+
+        return false;
+    }
+
+    bool finalizeOptions(IProperties *globals)
+    {
+        if (optInput.length())
+        {
+            const char *in = optInput.get();
+            while (*in && isspace(*in)) in++;
+            if (*in!='<')
+            {
+                StringBuffer content;
+                content.loadFile(in);
+                optInput.set(content.str());
+            }
+        }
+
+        if (optBindingId.isEmpty())
+            throw MakeStringException( 0, "ESDLBindingID must be provided!" );
+
+        if (optLogTransform.isEmpty() && (strncmp(optInput.str(), "<LogTransforms>", 15) != 0))
+            throw MakeStringException( 0, "Name of ESDL based LogTransform must be provided" );
+
+        return EsdlPublishCmdCommon::finalizeOptions(globals);
+    }
+};
+
+class EsdlUnBindLogTransformCmd : public EsdlBindLogTransformCmd
+{
+public:
+    int processCMD()
+    {
+        int success = -1;
+        Owned<IClientWsESDLConfig> esdlConfigClient = EsdlCmdHelper::getWsESDLConfigSoapService(optWSProcAddress, optWSProcPort, optUser, optPass);
+        Owned<IClientGetESDLBindingRequest> getrequest = esdlConfigClient->createGetESDLBindingRequest();
+        if (optVerbose)
+            fprintf(stdout,"\nFetching current ESDL binging configuration for (%s)\n", optBindingId.get());
+        getrequest->setEsdlBindingId(optBindingId.get());
+
+        Owned<IClientGetESDLBindingResponse> getresp = esdlConfigClient->GetESDLBinding(getrequest);
+
+        if (getresp->getExceptions().ordinality()>0)
+        {
+            EsdlCmdHelper::outputMultiExceptions(getresp->getExceptions());
+            return success;
+        }
+
+        if (getresp->getStatus().getCode()!=0)
+        {
+            fprintf(stderr, "\n Failed to retrieve ESDL Binding configuration for %s: %s.\n", optBindingId.get(), getresp->getStatus().getDescription());
+            return success;
+        }
+
+        const char * currentconfig = getresp->getConfigXML();
+        if (currentconfig && *currentconfig)
+        {
+            Owned<IPropertyTree> currconfigtree = createPTreeFromXMLString(currentconfig, ipt_caseInsensitive | ipt_ordered);
+            if (currconfigtree)
+            {
+                VStringBuffer xpath("Definition[1]/LogTransforms/LogTransform[@name='%s']", optLogTransform.get());
+
+                if (currconfigtree->hasProp(xpath.str()))
+                {
+                    if (currconfigtree->removeProp(xpath.str()))
+                    {
+                        StringBuffer newconfig;
+                        toXML(currconfigtree, newconfig);
+
+                        Owned<IClientPublishESDLBindingRequest> request = esdlConfigClient->createPublishESDLBindingRequest();
+
+                        if (optVerbose)
+                            fprintf(stdout,"\nAttempting to remove LogTransform '%s' from esdl binding '%s'\n", optLogTransform.get(), optBindingId.get());
+
+                        request->setEsdlDefinitionID(getresp->getESDLBinding().getDefinition().getId());
+                        request->setEsdlServiceName(getresp->getServiceName());
+                        request->setEspProcName(getresp->getEspProcName());
+                        request->setEspPort(getresp->getEspPort());
+                        request->setConfig(newconfig.str());
+                        request->setOverwrite(true);
+
+                        Owned<IClientPublishESDLBindingResponse> resp = esdlConfigClient->PublishESDLBinding(request);
+
+                        if (resp->getExceptions().ordinality() > 0)
+                        {
+                            EsdlCmdHelper::outputMultiExceptions(resp->getExceptions());
+                            return success;
+                        }
+
+                        if (resp->getStatus().getCode() == 0)
+                        {
+                            fprintf(stdout, "\nSuccessfully unbound LogTransform %s from ESDL Binding %s.\n", optLogTransform.get(), optBindingId.get());
+                            success = 0;
+                        }
+                        else
+                            fprintf(stderr, "\nCould not unbound LogTransform %s from ESDL Binding %s: %s\n", optLogTransform.get(), optBindingId.get(), resp->getStatus().getDescription());
+                    }
+                    else
+                        fprintf(stderr, "\n Could not remove LogTransform %s from ESDL Binding %s configuration.\n", optLogTransform.get(), optBindingId.get());
+                }
+                else
+                    fprintf(stderr, "\n LogTransform %s doesn't seem to be associated with ESDL Binding %s.\n", optLogTransform.get(), optBindingId.get());
+            }
+            else
+                fprintf(stderr, "\n Could not interpret configuration for ESDL Binding %s :  %s.\n", optBindingId.get(), currentconfig );
+        }
+        else
+            fprintf(stderr, "\n Received empty configuration for ESDL Binding %s.\n", optBindingId.get());
+
+        return success;
+    }
+
+    void usage()
+    {
+        printf( "\nUsage:\n\n"
+                "esdl unbind-log-transform <ESDLBindingID> <LogTransformName> [\n\n"
+                "   ESDLBindingID                              The id of the esdl binding associated with the target LogTransform\n"
+                "   LogTransformName                           The name of the target LogTransform (must exist in the service ESDL definition)\n");
+
+                EsdlPublishCmdCommon::usage();
+
+        printf( "\n   Use this command to unbind a LogTransform configuration currently associated with a given ESDL binding.\n"
+                "   To unbind a LogTransform, provide the target esdl binding id and the name of the LogTransform to unbind\n");
+
+        printf("\nExample:"
+                ">esdl unbind-log-transform myesp.8003.WsMyService myLogTransform\n"
+                );
+    }
+    bool parseCommandLineOptions(ArgvIterator &iter)
+    {
+        if (iter.done())
+        {
+            usage();
+            return false;
+        }
+
+        //First 2 parameter order is fixed.
+        for (int cur = 0; cur < 2 && !iter.done(); cur++)
+        {
+           const char *arg = iter.query();
+           if (*arg != '-')
+           {
+               switch (cur)
+               {
+                case 0:
+                    optBindingId.set(arg);
+                    break;
+                case 1:
+                    optLogTransform.set(arg);
+                    break;
+               }
+           }
+           else
+           {
+               fprintf(stderr, "\noption detected before required arguments: %s\n", arg);
+               usage();
+               return false;
+           }
+
+           iter.next();
+        }
+
+        for (; !iter.done(); iter.next())
+        {
+            if (parseCommandLineOption(iter))
+                continue;
+
+            if (matchCommandLineOption(iter, true)!=EsdlCmdOptionMatch)
+                return false;
+        }
+
+        return true;
+    }
+
+    bool finalizeOptions(IProperties *globals)
+    {
+        if(optBindingId.isEmpty())
+            throw MakeStringException( 0, "Name of Target ESDL Binding must be provided" );
+
+        if (optLogTransform.isEmpty())
+            throw MakeStringException( 0, "Name of ESDL based LogTransform must be provided" );
+
+        return EsdlPublishCmdCommon::finalizeOptions(globals);
+    }
+};
+
 class EsdlGetCmd : public EsdlPublishCmdCommon
 {
     protected:

+ 1 - 0
tools/esdlcmd/esdlcmd_common.hpp

@@ -103,6 +103,7 @@ typedef IEsdlCommand *(*EsdlCommandFactory)(const char *cmdname);
 #define ESDL_OPTION_ROLLUP              "--rollup"
 #define ESDL_OPTION_ECL_INCLUDE_LIST    "--ecl-imports"
 #define ESDL_OPTION_ECL_HEADER_BLOCK    "--ecl-header"
+#define ESDL_OPTION_ENCODED             "--encoded"
 
 #define ESDLOPT_INCLUDE_PATH            "--include-path"
 #define ESDLOPT_INCLUDE_PATH_S          "-I"

+ 4 - 0
tools/esdlcmd/esdlcmd_core.cpp

@@ -835,6 +835,10 @@ IEsdlCommand *createCoreEsdlCommand(const char *cmdname)
         return new EsdlListESDLDefCmd();
     if (strieq(cmdname, "LIST-BINDINGS"))
         return new EsdlListESDLBindingsCmd();
+    if (strieq(cmdname, "BIND-LOG-TRANSFORM"))
+        return new EsdlBindLogTransformCmd();
+    if (strieq(cmdname, "UNBIND-LOG-TRANSFORM"))
+        return new EsdlUnBindLogTransformCmd();
     if (strieq(cmdname, "MONITOR"))
         return createEsdlMonitorCommand(cmdname);
     if (strieq(cmdname, "MONITOR-TEMPLATE"))

+ 2 - 0
tools/esdlcmd/esdlcmd_shell.cpp

@@ -197,6 +197,8 @@ void EsdlCMDShell::usage()
            "   bind-method       Configure method associated with existing ESDL binding.\n"
            "   unbind-method     Remove method associated with existing ESDL binding.\n"
            "   get-binding       Get ESDL binding.\n"
+           "   bind-log-transform  	Configure log transform associated with existing ESDL binding.\n"
+           "   unbind-log-transform	Remove log transform associated with existing ESDL binding.\n"
            "   monitor           Generate ECL code for result monitoring / differencing\n"
            "   monitor-template  Generate a template for use with 'monitor' command\n"
            ""