loggingagent.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2014 HPCC Systems.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "httpclient.hpp"
  14. #include "ws_loggingservice_esp.ipp"
  15. #include "LoggingErrors.hpp"
  16. #include "loggingcommon.hpp"
  17. #include "loggingagentbase.hpp"
  18. #include "loggingagent.hpp"
  19. const int DefaultMaxTriesGTS = -1;
  20. const char* const PropESPServer = "ESPServer";
  21. const char* const PropServerUrl = "@url";
  22. const char* const PropServerUserID = "@user";
  23. const char* const PropServerPassword = "@password";
  24. const char* const PropServerWaitingSeconds = "MaxServerWaitingSeconds";
  25. const char* const MaxTriesGTS = "MaxTriesGTS";
  26. bool CESPServerLoggingAgent::init(const char * name, const char * type, IPropertyTree * cfg, const char * process)
  27. {
  28. if (!cfg)
  29. return false;
  30. IPropertyTree* espServer = cfg->queryBranch(PropESPServer);
  31. if(!espServer)
  32. throw MakeStringException(-1,"Unable to find ESPServer settings for log agent %s:%s", name, type);
  33. const char* url = espServer->queryProp(PropServerUrl);
  34. if (url && *url)
  35. serverUrl.set(url);
  36. const char* userID = espServer->queryProp(PropServerUserID);
  37. const char* password = espServer->queryProp(PropServerPassword);
  38. if (userID && *userID && password && *password)
  39. {
  40. serverUserID.set(userID);
  41. decrypt(serverPassword, password);
  42. }
  43. maxServerWaitingSeconds = cfg->getPropInt(PropServerWaitingSeconds);
  44. maxGTSRetries = cfg->getPropInt(MaxTriesGTS, DefaultMaxTriesGTS);
  45. readAllLogFilters(cfg);
  46. return true;
  47. }
  48. void CESPServerLoggingAgent::readAllLogFilters(IPropertyTree* cfg)
  49. {
  50. bool groupFilterRead = false;
  51. VStringBuffer xpath("Filters/Filter[@type='%s']", espLogContentGroupNames[ESPLCGBackEndResp]);
  52. IPropertyTree* filter = cfg->queryBranch(xpath.str());
  53. if (filter && filter->hasProp("@value"))
  54. {
  55. logBackEndResp = filter->getPropBool("@value");
  56. groupFilterRead = true;
  57. }
  58. for (unsigned i = 0; i < ESPLCGBackEndResp; i++)
  59. {
  60. if (readLogFilters(cfg, i))
  61. groupFilterRead = true;
  62. }
  63. if (!groupFilterRead)
  64. {
  65. groupFilters.clear();
  66. readLogFilters(cfg, ESPLCGAll);
  67. }
  68. }
  69. bool CESPServerLoggingAgent::readLogFilters(IPropertyTree* cfg, unsigned groupID)
  70. {
  71. Owned<CESPLogContentGroupFilters> espLogContentGroupFilters = new CESPLogContentGroupFilters((ESPLogContentGroup) groupID);
  72. StringBuffer xpath;
  73. if (groupID != ESPLCGAll)
  74. xpath.appendf("Filters/Filter[@type='%s']", espLogContentGroupNames[groupID]);
  75. else
  76. xpath.append("Filters/Filter");
  77. Owned<IPropertyTreeIterator> filters = cfg->getElements(xpath.str());
  78. ForEach(*filters)
  79. {
  80. IPropertyTree &filter = filters->query();
  81. StringBuffer value = filter.queryProp("@value");
  82. if (!value.length())
  83. continue;
  84. //clean "//"
  85. unsigned idx = value.length()-1;
  86. while (idx)
  87. {
  88. if ((value.charAt(idx-1) == '/') && (value.charAt(idx) == '/'))
  89. value.remove(idx, 1);
  90. idx--;
  91. }
  92. //clean "/*" at the end
  93. while ((value.length() > 1) && (value.charAt(value.length()-2) == '/') && (value.charAt(value.length()-1) == '*'))
  94. value.setLength(value.length() - 2);
  95. if (value.length() && !streq(value.str(), "*") && !streq(value.str(), "/") && !streq(value.str(), "*/"))
  96. {
  97. espLogContentGroupFilters->addFilter(value.str());
  98. }
  99. else
  100. {
  101. espLogContentGroupFilters->clearFilters();
  102. break;
  103. }
  104. }
  105. bool hasFilter = espLogContentGroupFilters->getFilterCount() > 0;
  106. if (hasFilter)
  107. groupFilters.append(*espLogContentGroupFilters.getClear());
  108. return hasFilter;
  109. }
  110. bool CESPServerLoggingAgent::getTransactionSeed(IEspGetTransactionSeedRequest& req, IEspGetTransactionSeedResponse& resp)
  111. {
  112. bool bRet = false;
  113. StringBuffer soapreq(
  114. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  115. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  116. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  117. " <soap:Body>");
  118. soapreq.append("<GetTransactionSeedRequest/>");
  119. soapreq.append("</soap:Body></soap:Envelope>");
  120. unsigned retry = 1;
  121. while (1)
  122. {
  123. try
  124. {
  125. int statusCode = 0;
  126. StringBuffer statusMessage, transactionSeed;
  127. if (!getTransactionSeed(soapreq, statusCode, statusMessage, transactionSeed))
  128. throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed,"Failed to get TransactionSeed");
  129. resp.setSeedId(transactionSeed.str());
  130. resp.setStatusCode(statusCode);
  131. if (statusMessage.length())
  132. resp.setStatusMessage(statusMessage.str());
  133. bRet = true;
  134. break;
  135. }
  136. catch (IException* e)
  137. {
  138. StringBuffer errorStr, errorMessage;
  139. errorMessage.append("Failed to get TransactionSeed: error code ").append(e->errorCode()).append(", error message ").append(e->errorMessage(errorStr));
  140. ERRLOG("%s -- try %d", errorMessage.str(), retry);
  141. e->Release();
  142. if (retry < maxGTSRetries)
  143. {
  144. Sleep(retry*3000);
  145. retry++;
  146. }
  147. else
  148. {
  149. resp.setStatusCode(-1);
  150. resp.setStatusMessage(errorMessage.str());
  151. break;
  152. }
  153. }
  154. }
  155. return bRet;
  156. }
  157. bool CESPServerLoggingAgent::getTransactionSeed(StringBuffer& soapreq, int& statusCode, StringBuffer& statusMessage, StringBuffer& seedID)
  158. {
  159. StringBuffer status, response;
  160. if (!sendHTTPRequest(soapreq, response, status) || !response.length() || !status.length())
  161. throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "Failed to send Transaction Seed request to %s", serverUrl.str());
  162. if (!strieq(status, "200 OK"))
  163. throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "%s", status.str());
  164. Owned<IPropertyTree> pTree = createPTreeFromXMLString(response.str());
  165. if (!pTree)
  166. throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "Failed to read response from %s", serverUrl.str());
  167. statusCode = pTree->getPropInt("soap:Body/GetTransactionSeedResponse/StatusCode");
  168. statusMessage.set(pTree->queryProp("soap:Body/GetTransactionSeedResponse/StatusMessage"));
  169. seedID.set(pTree->queryProp("soap:Body/GetTransactionSeedResponse/SeedId"));
  170. if (statusCode || !seedID.length())
  171. throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "Failed to get Transaction Seed from %s", serverUrl.str());
  172. return true;
  173. }
  174. bool CESPServerLoggingAgent::updateLog(IEspUpdateLogRequestWrap& req, IEspUpdateLogResponse& resp)
  175. {
  176. try
  177. {
  178. StringBuffer soapreq(
  179. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
  180. "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""
  181. " xmlns:SOAP-ENC=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  182. " <soap:Body>"
  183. );
  184. soapreq.append(req.getUpdateLogRequest());
  185. soapreq.append("</soap:Body></soap:Envelope>");
  186. StringBuffer status, respStr;
  187. if (sendHTTPRequest(soapreq, respStr, status) && status.length() && strieq(status, "200 OK"))
  188. resp.setStatusCode(0);
  189. else if (status.length() && !strieq(status, "200 OK"))
  190. throw MakeStringException(EspLoggingErrors::UpdateLogFailed, "%s", status.str());
  191. else if (respStr.length())
  192. throw MakeStringException(EspLoggingErrors::UpdateLogFailed, "%s", respStr.str());
  193. else
  194. throw MakeStringException(EspLoggingErrors::UpdateLogFailed, "Failed to send update log request to %s", serverUrl.str());
  195. }
  196. catch (IException* e)
  197. {//retry will be in update log queue.
  198. StringBuffer errorStr, errorMessage;
  199. errorMessage.append("Failed to update log: error code ").append(e->errorCode()).append(", error message ").append(e->errorMessage(errorStr));
  200. ERRLOG("%s", errorMessage.str());
  201. resp.setStatusCode(-1);
  202. resp.setStatusMessage(errorMessage.str());
  203. e->Release();
  204. }
  205. return true;
  206. }
  207. void CESPServerLoggingAgent::addLogContentBranch(StringArray& branchNames, IPropertyTree* contentToLogBranch, IPropertyTree* updateLogRequestTree)
  208. {
  209. IPropertyTree* pTree = updateLogRequestTree;
  210. unsigned numOfBranchNames = branchNames.length();
  211. if (numOfBranchNames > 0)
  212. {
  213. unsigned i = 0;
  214. while (i < numOfBranchNames)
  215. {
  216. const char* branchName = branchNames.item(i);
  217. if (branchName && *branchName)
  218. pTree = ensurePTree(pTree, branchName);
  219. i++;
  220. }
  221. }
  222. pTree->addPropTree(contentToLogBranch->queryName(), LINK(contentToLogBranch));
  223. }
  224. void CESPServerLoggingAgent::filterAndAddLogContentBranch(StringArray& branchNamesInFilter, unsigned idx,
  225. StringArray& branchNamesInLogContent, IPropertyTree* originalLogContentBranch, IPropertyTree* updateLogRequestTree, bool& logContentEmpty)
  226. {
  227. Owned<IPropertyTreeIterator> contentItr = originalLogContentBranch->getElements(branchNamesInFilter.item(idx));
  228. ForEach(*contentItr)
  229. {
  230. IPropertyTree& contentToLogBranch = contentItr->query();
  231. if (idx == branchNamesInFilter.length() - 1)
  232. {
  233. addLogContentBranch(branchNamesInLogContent, &contentToLogBranch, updateLogRequestTree);
  234. logContentEmpty = false;
  235. }
  236. else
  237. {
  238. branchNamesInLogContent.append(contentToLogBranch.queryName());
  239. filterAndAddLogContentBranch(branchNamesInFilter, idx+1, branchNamesInLogContent, &contentToLogBranch,
  240. updateLogRequestTree, logContentEmpty);
  241. branchNamesInLogContent.remove(branchNamesInLogContent.length() - 1);
  242. }
  243. }
  244. }
  245. void CESPServerLoggingAgent::filterLogContentTree(StringArray& filters, IPropertyTree* originalContentTree, IPropertyTree* newLogContentTree, bool& logContentEmpty)
  246. {
  247. ForEachItemIn(i, filters)
  248. {
  249. const char* logContentFilter = filters.item(i);
  250. if(!logContentFilter || !*logContentFilter)
  251. continue;
  252. StringArray branchNamesInFilter, branchNamesInLogContent;
  253. branchNamesInFilter.appendListUniq(logContentFilter, "/");
  254. filterAndAddLogContentBranch(branchNamesInFilter, 0, branchNamesInLogContent, originalContentTree, newLogContentTree, logContentEmpty);
  255. }
  256. }
  257. void CESPServerLoggingAgent::filterLogContent(IEspUpdateLogRequestWrap* req)
  258. {
  259. const char* logContent = req->getUpdateLogRequest();
  260. Owned<IPropertyTree> updateLogRequestTree = createPTree("UpdateLogRequest");
  261. if (groupFilters.length() < 1)
  262. {//No filter
  263. if (logContent && *logContent)
  264. {
  265. Owned<IPropertyTree> pTree = createPTreeFromXMLString(logContent);
  266. updateLogRequestTree->addPropTree(pTree->queryName(), LINK(pTree));
  267. }
  268. else
  269. {
  270. Owned<IPropertyTree> espContext = req->getESPContext();
  271. Owned<IPropertyTree> userContext = req->getUserContext();
  272. Owned<IPropertyTree> userRequest = req->getUserRequest();
  273. const char* userResp = req->getUserResponse();
  274. const char* backEndResp = req->getBackEndResponse();
  275. if (!espContext && !userContext && !userRequest && (!userResp || !*userResp) && (!backEndResp || !*backEndResp))
  276. throw MakeStringException(EspLoggingErrors::UpdateLogFailed, "Failed to read log content");
  277. StringBuffer espContextXML, userContextXML, userRequestXML;
  278. IPropertyTree* logContentTree = ensurePTree(updateLogRequestTree, "LogContent");
  279. if (espContext)
  280. {
  281. logContentTree->addPropTree(espContext->queryName(), LINK(espContext));
  282. }
  283. if (userContext)
  284. {
  285. IPropertyTree* pTree = ensurePTree(logContentTree, espLogContentGroupNames[ESPLCGUserContext]);
  286. pTree->addPropTree(userContext->queryName(), LINK(userContext));
  287. }
  288. if (userRequest)
  289. {
  290. IPropertyTree* pTree = ensurePTree(logContentTree, espLogContentGroupNames[ESPLCGUserReq]);
  291. pTree->addPropTree(userRequest->queryName(), LINK(userRequest));
  292. }
  293. if (userResp && *userResp)
  294. {
  295. IPropertyTree* pTree = ensurePTree(logContentTree, espLogContentGroupNames[ESPLCGUserResp]);
  296. Owned<IPropertyTree> userRespTree = createPTreeFromXMLString(userResp);
  297. pTree->addPropTree(userRespTree->queryName(), LINK(userRespTree));
  298. }
  299. if (backEndResp && *backEndResp)
  300. logContentTree->addProp(espLogContentGroupNames[ESPLCGBackEndResp], backEndResp);
  301. }
  302. }
  303. else
  304. {
  305. bool logContentEmpty = true;
  306. IPropertyTree* logContentTree = ensurePTree(updateLogRequestTree, "LogContent");
  307. if (logContent && *logContent)
  308. {
  309. Owned<IPropertyTree> originalContentTree = createPTreeFromXMLString(logContent);
  310. filterLogContentTree(groupFilters.item(0).getFilters(), originalContentTree, logContentTree, logContentEmpty);
  311. }
  312. else
  313. {
  314. for (unsigned group = 0; group < ESPLCGBackEndResp; group++)
  315. {
  316. Owned<IPropertyTree> originalContentTree;
  317. if (group == ESPLCGESPContext)
  318. originalContentTree.setown(req->getESPContext());
  319. else if (group == ESPLCGUserContext)
  320. originalContentTree.setown(req->getUserContext());
  321. else if (group == ESPLCGUserReq)
  322. originalContentTree.setown(req->getUserRequest());
  323. else //group = ESPLCGUserResp
  324. {
  325. const char* resp = req->getUserResponse();
  326. if (!resp || !*resp)
  327. continue;
  328. originalContentTree.setown(createPTreeFromXMLString(resp));
  329. }
  330. if (!originalContentTree)
  331. continue;
  332. IPropertyTree* newContentTree = ensurePTree(logContentTree, espLogContentGroupNames[group]);
  333. bool hasFilters = false;
  334. ForEachItemIn(i, groupFilters)
  335. {
  336. CESPLogContentGroupFilters& filtersGroup = groupFilters.item(i);
  337. if (filtersGroup.getGroup() == group)
  338. {
  339. if (group != ESPLCGESPContext)//For non ESPLCGESPContext, we want to keep the root of original tree.
  340. newContentTree = ensurePTree(newContentTree, originalContentTree->queryName());
  341. filterLogContentTree(filtersGroup.getFilters(), originalContentTree, newContentTree, logContentEmpty);
  342. hasFilters = true;
  343. break;
  344. }
  345. }
  346. if (!hasFilters)
  347. {
  348. newContentTree->addPropTree(originalContentTree->queryName(), LINK(originalContentTree));
  349. logContentEmpty = false;
  350. }
  351. }
  352. if (logBackEndResp)
  353. {
  354. const char* resp = req->getBackEndResponse();
  355. if (resp && *resp)
  356. {
  357. logContentTree->addProp(espLogContentGroupNames[ESPLCGBackEndResp], resp);
  358. logContentEmpty = false;
  359. }
  360. }
  361. }
  362. if (logContentEmpty)
  363. throw MakeStringException(EspLoggingErrors::UpdateLogFailed, "Failed to read log content");
  364. }
  365. const char* option = req->getOption();
  366. if (option && *option)
  367. updateLogRequestTree->addProp("Option", option);
  368. StringBuffer updateLogRequestXML;
  369. toXML(updateLogRequestTree, updateLogRequestXML);
  370. DBGLOG("filtered content and option: <%s>", updateLogRequestXML.str());
  371. req->clearOriginalContent();
  372. req->setUpdateLogRequest(updateLogRequestXML.str());
  373. }
  374. bool CESPServerLoggingAgent::sendHTTPRequest(StringBuffer& req, StringBuffer &resp, StringBuffer &status)
  375. {
  376. Owned<IHttpClientContext> httpctx = getHttpClientContext();
  377. Owned <IHttpClient> httpclient = httpctx->createHttpClient(NULL, serverUrl.str());
  378. if (serverUserID.length() && serverPassword.length())
  379. {
  380. httpclient->setUserID(serverUserID.str());
  381. httpclient->setPassword(serverPassword.str());
  382. }
  383. httpclient->setTimeOut(maxServerWaitingSeconds);
  384. return !httpclient->sendRequest("POST", "text/xml", req, resp, status);
  385. }
  386. extern "C"
  387. {
  388. ESPSERVERLOGGINGAGENT_API IEspLogAgent* newLoggingAgent()
  389. {
  390. return new CESPServerLoggingAgent();
  391. }
  392. }