All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.cloud.autoscaling.SystemLogListener Maven / Gradle / Ivy

There is a newer version: 9.7.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.cloud.autoscaling;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;

import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
import org.apache.solr.client.solrj.cloud.autoscaling.TriggerEventProcessorStage;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.params.CollectionAdminParams;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.Utils;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.util.IdUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This listener saves events to the {@link CollectionAdminParams#SYSTEM_COLL} collection.
 * 

Configuration properties:

*
    *
  • collection - optional string, specifies what collection should be used for storing events. Default value * is {@link CollectionAdminParams#SYSTEM_COLL}.
  • *
*/ public class SystemLogListener extends TriggerListenerBase { private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static final String SOURCE_FIELD = "source_s"; public static final String EVENT_SOURCE_FIELD = "event.source_s"; public static final String EVENT_TYPE_FIELD = "event.type_s"; public static final String STAGE_FIELD = "stage_s"; public static final String ACTION_FIELD = "action_s"; public static final String MESSAGE_FIELD = "message_t"; public static final String BEFORE_ACTIONS_FIELD = "before.actions_ss"; public static final String AFTER_ACTIONS_FIELD = "after.actions_ss"; public static final String COLLECTIONS_FIELD = "collections_ss"; public static final String SOURCE = SystemLogListener.class.getSimpleName(); public static final String DOC_TYPE = "autoscaling_event"; private String collection = CollectionAdminParams.SYSTEM_COLL; @Override public void configure(SolrResourceLoader loader, SolrCloudManager cloudManager, AutoScalingConfig.TriggerListenerConfig config) throws TriggerValidationException { super.configure(loader, cloudManager, config); collection = (String)config.properties.getOrDefault(CollectionAdminParams.COLLECTION, CollectionAdminParams.SYSTEM_COLL); } @Override public void onEvent(TriggerEvent event, TriggerEventProcessorStage stage, String actionName, ActionContext context, Throwable error, String message) throws Exception { try { ClusterState clusterState = cloudManager.getClusterStateProvider().getClusterState(); DocCollection coll = clusterState.getCollectionOrNull(collection); if (coll == null) { log.debug("Collection {} missing, skip sending event {}", collection, event); return; } SolrInputDocument doc = new SolrInputDocument(); doc.addField(CommonParams.TYPE, DOC_TYPE); doc.addField(SOURCE_FIELD, SOURCE); doc.addField("id", IdUtils.timeRandomId()); doc.addField("event.id_s", event.getId()); doc.addField(EVENT_TYPE_FIELD, event.getEventType().toString()); doc.addField(EVENT_SOURCE_FIELD, event.getSource()); doc.addField("event.time_l", event.getEventTime()); doc.addField("timestamp", new Date()); addMap("event.property.", doc, event.getProperties()); doc.addField(STAGE_FIELD, stage.toString()); if (actionName != null) { doc.addField(ACTION_FIELD, actionName); } if (message != null) { doc.addField(MESSAGE_FIELD, message); } addError(doc, error); // add JSON versions of event and context String eventJson = Utils.toJSONString(event); doc.addField("event_str", eventJson); if (context != null) { // capture specifics of operations after compute_plan action addOperations(doc, (List)context.getProperties().get("operations")); // capture specifics of responses after execute_plan action addResponses(doc, (List>)context.getProperties().get("responses")); addActions(BEFORE_ACTIONS_FIELD, doc, (List)context.getProperties().get(TriggerEventProcessorStage.BEFORE_ACTION.toString())); addActions(AFTER_ACTIONS_FIELD, doc, (List)context.getProperties().get(TriggerEventProcessorStage.AFTER_ACTION.toString())); String contextJson = Utils.toJSONString(context); doc.addField("context_str", contextJson); } UpdateRequest req = new UpdateRequest(); req.add(doc); req.setParam(CollectionAdminParams.COLLECTION, collection); cloudManager.request(req); } catch (Exception e) { if ((e instanceof SolrException) && e.getMessage().contains("Collection not found")) { // relatively benign but log this - collection still existed when we started log.info("Collection {} missing, skip sending event {}", collection, event); } else { log.warn("Exception sending event. Collection: {}, event: {}, exception: {}", collection, event, e); } } } private void addActions(String field, SolrInputDocument doc, List actions) { if (actions == null) { return; } actions.forEach(a -> doc.addField(field, a)); } private void addMap(String prefix, SolrInputDocument doc, Map map) { map.forEach((k, v) -> { if (v instanceof Collection) { for (Object o : (Collection)v) { doc.addField(prefix + k + "_ss", String.valueOf(o)); } } else { doc.addField(prefix + k + "_ss", String.valueOf(v)); } }); } private void addOperations(SolrInputDocument doc, List operations) { if (operations == null || operations.isEmpty()) { return; } Set collections = new HashSet<>(); for (SolrRequest req : operations) { SolrParams params = req.getParams(); if (params == null) { continue; } if (params.get(CollectionAdminParams.COLLECTION) != null) { collections.add(params.get(CollectionAdminParams.COLLECTION)); } // build a whitespace-separated param string StringJoiner paramJoiner = new StringJoiner(" "); paramJoiner.setEmptyValue(""); for (Iterator it = params.getParameterNamesIterator(); it.hasNext(); ) { final String name = it.next(); final String [] values = params.getParams(name); for (String value : values) { paramJoiner.add(name + "=" + value); } } String paramString = paramJoiner.toString(); if (!paramString.isEmpty()) { doc.addField("operations.params_ts", paramString); } } if (!collections.isEmpty()) { doc.addField(COLLECTIONS_FIELD, collections); } } private void addResponses(SolrInputDocument doc, List> responses) { if (responses == null || responses.isEmpty()) { return; } for (NamedList rsp : responses) { Object o = rsp.get("success"); if (o != null) { doc.addField("responses_ts", "success " + o); } else { o = rsp.get("failure"); if (o != null) { doc.addField("responses_ts", "failure " + o); } else { // something else doc.addField("responses_ts", Utils.toJSONString(rsp)); } } } } private void addError(SolrInputDocument doc, Throwable error) { if (error == null) { return; } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); error.printStackTrace(pw); pw.flush(); pw.close(); doc.addField("error.message_t", error.getMessage()); doc.addField("error.details_t", sw.toString()); } }