cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

This product reached the end of support date on March 31, 2021.

Dynatrace integration with slack

tarjei_utnes
Organizer

Hi, I see that Dynatrace has an integration for Slack.

Is it possible to use the same integration for AppMon?

15 REPLIES 15

saanjeith_varat
Inactive

It is not currently possible to use the WebHook created for Dynatrace with AppMon. I don't know if anyone has built a home-grown integration but I would love to see an integration with Slack + AppMon in the future.

I have started to look into it. I think the HipChat plugin is actually quite similar, it only needs a little tweaking.

Awesome!! Please keep me up to date when you have a completed solution Tareji.

Thanks,

Sanj

tarjei_utnes
Organizer

So now I have it working (when i test it atleast)

Have installed it in an environment now to see.

https://redocean.slack.com/services/new/incoming-w...

You can download it from this link:

https://www.dropbox.com/sh/wx93iayxeq2zal4/AADV1Ch...

It is based on HipChat

Cool stuff. I will connect you with @Ingo Hackl who can also get you an official community plugin page and put this stuff on GitHub so that everyone can find and eventually extend it

tarjei_utnes
Organizer

I also built a version for DT 6.2 in the dropbox there. (or you can download and build yourself)

tarjei_utnes
Organizer

Ok, so there is a Git repository for the plugin here: https://github.com/dynaTrace/Dynatrace-Slack-Integration-Plugin If you want to try it please go ahead, I am working on updating the community page with information on how to configure it.

Hi Tarjei,

Our dynatrace server does not have internet acecss. will this plugin work with proxy? how do we set it up.

Thanks

Aftab

tarjei_utnes
Organizer

Hi,

I believe that this is not something that is implemented now. But that maybe this parameter may help you

-Djava.net.useSystemProxies=true

http://docs.oracle.com/javase/7/docs/api/java/net/...

Can you test it and report back?

aftab_alam
Organizer

I modified plugin and added option to give proxy.

I had to change code to make sure it can call https URL of slack via proxy without any cert.

attaching java code as well


/**
* This template file was generated by dynaTrace client.
* The dynaTrace community portal can be found here: http://community.compuwareapm.com/
* For information how to publish a plugin please visit http://community.compuwareapm.com/plugins/contribute/
**/


package com.Dynatrace;


import com.dynatrace.diagnostics.pdk.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;


import java.net.URL;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.logging.Logger;


import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;




public class SlackChat implements ActionV2 {


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


/**
* Initializes the Plugin. This method is called in the following cases:
* <ul>
* <li>before <tt>execute</tt> is called the first time for this
* scheduled Plugin</li>
* <li>before the next <tt>execute</tt> if <tt>teardown</tt> was called
* after the last execution</li>
* </ul>
*
* <p>
* If the returned status is <tt>null</tt> or the status code is a
* non-success code then {@link #teardown(ActionEnvironment)} will be called
* next.
*
* <p>
* Resources like sockets or files can be opened in this method.
* Resources like sockets or files can be opened in this method.
* @param env
* the configured <tt>ActionEnvironment</tt> for this Plugin
* @see #teardown(ActionEnvironment)
* @return a <tt>Status</tt> object that describes the result of the
* method call
*/
@Override
public Status setup(ActionEnvironment env) throws Exception {
// TODO
return new Status(Status.StatusCode.Success);
}


/**
* Executes the Action Plugin to process incidents.
*
* <p>
* This method may be called at the scheduled intervals, but only if incidents
* occurred in the meantime. If the Plugin execution takes longer than the
* schedule interval, subsequent calls to
* {@link #execute(ActionEnvironment)} will be skipped until this method
* returns. After the execution duration exceeds the schedule timeout,
* {@link ActionEnvironment#isStopped()} will return <tt>true</tt>. In this
* case execution should be stopped as soon as possible. If the Plugin
* ignores {@link ActionEnvironment#isStopped()} or fails to stop execution in
* a reasonable timeframe, the execution thread will be stopped ungracefully
* which might lead to resource leaks!
*
* @param env
* a <tt>ActionEnvironment</tt> object that contains the Plugin
* configuration and incidents
* @return a <tt>Status</tt> object that describes the result of the
* method call
*/
@Override
public Status execute(ActionEnvironment env) throws Exception {
/*
// this sample shows how to receive and act on incidents
Collection<Incident> incidents = env.getIncidents();
for (Incident incident : incidents) {
String message = incident.getMessage();
log.info("Incident " + message + " triggered.");
for (Violation violation : incident.getViolations()) {
log.info("Measure " + violation.getViolatedMeasure().getName() + " violoated threshold.");
}
}
*/


//MAP ALL INCIDENTS A COLLECTION
Collection<Incident> incidents = env.getIncidents();


//FOR EACH INCIDENT
for (Incident incident : incidents) {


//LOG INCIDENT MESSAGE
String message = incident.getMessage();
log.fine("Incident " + message + " triggered.");


//SET INPUT FIELDS
URL url = env.getConfigUrl("url");
boolean notifyAll = env.getConfigBoolean("notifyAll");
String dashboard = env.getConfigString("linkedDashboard");
// set proxy start
boolean useProxy = env.getConfigBoolean("isProxy");
String proxyhost = env.getConfigString("proxy_server");
String proxyport = env.getConfigString("proxy_port");


if (useProxy){
log.fine("setting up proxy host " + proxyhost);
System.setProperty("https.proxyHost", proxyhost);
System.setProperty("https.proxyPort", proxyport);
System.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");


log.fine("GETTING up proxy host " + System.getProperty("https.proxyHost"));


// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};


// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());


// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};


// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} // set proxy start


//OPEN URL CONNECTION AND SET TIMEOUTS - USES CONNECTION METHOD 'POST'
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setConnectTimeout(5000);
con.setReadTimeout(20000);


//SET VARIABLES
OutputStream out;
InputStream in;
int responseCode;
String responseBody;


//JSON CREATION
JSONObject jsonObj = new JSONObject();
String state = "";
// Compose string chat_message => This message will be sent to the SlackChat channel
if (notifyAll) {
state = "<!channel> ";
}
if (incident.isOpen()) {
state = state + "dynatrace incident triggered:";
} else if (incident.isClosed()) {
state = state + "dynatrace incident ended:";
}
String title = incident.getIncidentRule().getName();
String chat_message = "";
//chat_message = chat_message + " <ul>";
//chat_message = chat_message + "Incident UUID: " + incident.getKey().getUUID() + "\n";


chat_message = chat_message + "Incident start: " + incident.getStartTime() + "\n";
chat_message = chat_message + "Incident end: " + incident.getEndTime() + "\n";






// chat_message = chat_message + "<li><strong>Status state code:</strong> " + incident.getState() + "</li>";




chat_message = chat_message + "Message: " + message + "\n";


for (Violation violation : incident.getViolations()) {
chat_message = chat_message + "Violated Measure: " + violation.getViolatedMeasure().getName() + " - Threshold: " + violation.getViolatedThreshold().getValue() + "\n";
}


//chat_message = chat_message + "</ul>";


/*
* Create JSON Object => Will be sent to SlackChat via HTTP POST
*/
String severity = incident.getSeverity().toString();
String color = "good";
switch (severity) {
case "Error" : color = "danger";
break;
case "Warning" : color = "warning";
break;
default : color = "good";
}


JSONObject attachment = new JSONObject();
attachment.put("title", title);
attachment.put("color", color);
attachment.put("text", chat_message);
if (!(dashboard == null||dashboard.equals("")||dashboard.isEmpty())) {
attachment.put("title_link", "http://" + incident.getServerName() + "/rest/management/reports/create/" + URLEncoder.encode(dashboard, "UTF-8").replaceAll("\\+", "%20"));
}
JSONArray attachArray = new JSONArray();


attachArray.add(attachment);


jsonObj.put("username", "dynatrace");
jsonObj.put("icon_url", "https://media.glassdoor.com/sqll/309684/dynatrace-squarelogo-1458744847928.png");
jsonObj.put("text", state);
jsonObj.put("attachments", attachArray);






//JSON TO STRING
String jsonString = jsonObj.toJSONString();


//LOG JSON STRING
log.fine("JSON String is: " + jsonString);


//JSON STRING TO BYTES
byte[] payload = jsonString.getBytes();


//SET CONNECTION OUTPUT
con.setFixedLengthStreamingMode(payload.length);
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
con.setDoOutput(true);


//LOG PROGRESS
log.fine("Trying to get output stream...");


//TRY TO GET OUTPUT STREAM
try{
out = con.getOutputStream();
}


//CATCH EXCEPTION, LOG IT THEN SEND RESPONSE ERROR CODE
catch (IOException e){
log.severe("Exception thrown whilst getting output stream...");
log.severe(e.toString());
con.disconnect();
return new Status (Status.StatusCode.ErrorInternalException);
}


//LOG PROGRESS
log.fine("Trying to write to output stream");


//TRY TO SEND PAYLOAD
try{
out.write(payload);
out.close();
}


//CATCH EXCEPTION, LOG IT THEN SEND RESPONSE ERROR CODE
catch (IOException e) {
log.severe("Exception thrown whilst writing to output stream...");
log.severe(e.toString());
con.disconnect();
return new Status (Status.StatusCode.ErrorInternalException);
}


//LOG PROGRESS
log.fine("Trying to connect...");




//TRY TO GET RESPONSE CODE
try{
responseCode = con.getResponseCode();
log.fine("Response Code : " + responseCode);
}


//CATCH EXCEPTION, LOG IT THEN SEND RESPONSE ERROR CODE
catch (IOException e) {
log.severe("Exception thrown whilst writing to output stream...");
log.severe(e.toString());
con.disconnect();
return new Status (Status.StatusCode.ErrorInternalException);
}


// //TRY TO GET INPUT STREAM
// try{
// if(responseCode == 200){
// in = con.getInputStream();
// }
// else{
// in = con.getErrorStream();
// }
//
// BufferedReader bufferReader = new BufferedReader(new InputStreamReader(in));
// responseBody = bufferReader.readLine();
// bufferReader.close();
// if(responseCode != 200){
// log.warning("Response code was: " + responseCode);
// log.warning("Error received from PagerDuty: " + responseBody);
// }
// }
//
// //CATCH EXCEPTION, LOG IT THEN SEND RESPONSE ERROR CODE
// catch (IOException e) {
// log.severe("Exception thrown whilst reading from input stream...");
// log.severe(e.toString());
// return new Status (Status.StatusCode.ErrorInternalException);
// }


//DISCONNECT
finally{
con.disconnect();
}
}


return new Status(Status.StatusCode.Success);
}


/**
* Shuts the Plugin down and frees resources. This method is called in the
* following cases:
* <ul>
* <li>the <tt>setup</tt> method failed</li>
* <li>the Plugin configuration has changed</li>
* <li>the execution duration of the Plugin exceeded the schedule timeout</li>
* <li>the schedule associated with this Plugin was removed</li>
* </ul>
* <p>
* The Plugin methods <tt>setup</tt>, <tt>execute</tt> and
* <tt>teardown</tt> are called on different threads, but they are called
* sequentially. This means that the execution of these methods does not
* overlap, they are executed one after the other.
*
* <p>
* Examples:
* <ul>
* <li><tt>setup</tt> (failed) -> <tt>teardown</tt></li>
* <li><tt>execute</tt> starts, configuration changes, <tt>execute</tt>
* ends -> <tt>teardown</tt><br>
* on next schedule interval: <tt>setup</tt> -> <tt>execute</tt> ...</li>
* <li><tt>execute</tt> starts, execution duration timeout,
* <tt>execute</tt> stops -> <tt>teardown</tt></li>
* <li><tt>execute</tt> starts, <tt>execute</tt> ends, schedule is
* removed -> <tt>teardown</tt></li>
* </ul>
* Failed means that either an unhandled exception is thrown or the status
* returned by the method contains a non-success code.
*
* <p>
* All by the Plugin allocated resources should be freed in this method.
* Examples are opened sockets or files.
*
* @see #setup(ActionEnvironment)
*/
@Override
public void teardown(ActionEnvironment env) throws Exception {
// TODO
}
}



we should add system profile and application name in slack message as well( similar to email alert)

email

I could not get your code to work. The plugin would fail to load correctly under appMon 7.0.2.

stuart_marsh
Newcomer

Build for appMon 7.0.2.1011

comslackchat-0102jar.zip

Did it work when you built it again?

stuart_marsh
Newcomer

Yes tested ok here. Note that it wont work if your proxy expects authentication.